winealsa: Move get_prop_value to the unixlib.
Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Andrew Eikum <aeikum@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
ab9fa4c679
commit
c6bfab8739
|
@ -34,13 +34,13 @@
|
||||||
#include "windef.h"
|
#include "windef.h"
|
||||||
#include "winbase.h"
|
#include "winbase.h"
|
||||||
#include "winternl.h"
|
#include "winternl.h"
|
||||||
|
#include "initguid.h"
|
||||||
#include "mmdeviceapi.h"
|
#include "mmdeviceapi.h"
|
||||||
|
|
||||||
#include "wine/debug.h"
|
#include "wine/debug.h"
|
||||||
#include "wine/list.h"
|
#include "wine/list.h"
|
||||||
#include "wine/unixlib.h"
|
#include "wine/unixlib.h"
|
||||||
|
|
||||||
#include "initguid.h"
|
|
||||||
#include "unixlib.h"
|
#include "unixlib.h"
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(alsa);
|
WINE_DEFAULT_DEBUG_CHANNEL(alsa);
|
||||||
|
@ -2241,6 +2241,184 @@ static NTSTATUS is_started(void *args)
|
||||||
return alsa_unlock_result(stream, ¶ms->result, stream->started ? S_OK : S_FALSE);
|
return alsa_unlock_result(stream, ¶ms->result, stream->started ? S_OK : S_FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static unsigned int alsa_probe_num_speakers(char *name)
|
||||||
|
{
|
||||||
|
snd_pcm_t *handle;
|
||||||
|
snd_pcm_hw_params_t *params;
|
||||||
|
int err;
|
||||||
|
unsigned int max_channels = 0;
|
||||||
|
|
||||||
|
if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
|
||||||
|
WARN("The device \"%s\" failed to open: %d (%s).\n",
|
||||||
|
name, err, snd_strerror(err));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
params = malloc(snd_pcm_hw_params_sizeof());
|
||||||
|
if (!params) {
|
||||||
|
WARN("Out of memory.\n");
|
||||||
|
snd_pcm_close(handle);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
|
||||||
|
WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
|
||||||
|
name, err, snd_strerror(err));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((err = snd_pcm_hw_params_get_channels_max(params,
|
||||||
|
&max_channels)) < 0){
|
||||||
|
WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
free(params);
|
||||||
|
snd_pcm_close(handle);
|
||||||
|
|
||||||
|
return max_channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AudioDeviceConnectionType {
|
||||||
|
AudioDeviceConnectionType_Unknown = 0,
|
||||||
|
AudioDeviceConnectionType_PCI,
|
||||||
|
AudioDeviceConnectionType_USB
|
||||||
|
};
|
||||||
|
|
||||||
|
static NTSTATUS get_prop_value(void *args)
|
||||||
|
{
|
||||||
|
struct get_prop_value_params *params = args;
|
||||||
|
const char *name = params->alsa_name;
|
||||||
|
EDataFlow flow = params->flow;
|
||||||
|
const GUID *guid = params->guid;
|
||||||
|
const PROPERTYKEY *prop = params->prop;
|
||||||
|
PROPVARIANT *out = params->value;
|
||||||
|
static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
|
||||||
|
{0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
|
||||||
|
};
|
||||||
|
|
||||||
|
if(IsEqualPropertyKey(*prop, devicepath_key))
|
||||||
|
{
|
||||||
|
char uevent[MAX_PATH];
|
||||||
|
FILE *fuevent;
|
||||||
|
int card, device;
|
||||||
|
|
||||||
|
/* only implemented for identifiable devices, i.e. not "default" */
|
||||||
|
if(!sscanf(name, "plughw:%u,%u", &card, &device)){
|
||||||
|
params->result = E_NOTIMPL;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
|
||||||
|
fuevent = fopen(uevent, "r");
|
||||||
|
|
||||||
|
if(fuevent){
|
||||||
|
enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
|
||||||
|
USHORT vendor_id = 0, product_id = 0;
|
||||||
|
char line[256];
|
||||||
|
|
||||||
|
while (fgets(line, sizeof(line), fuevent)) {
|
||||||
|
char *val;
|
||||||
|
size_t val_len;
|
||||||
|
|
||||||
|
if((val = strchr(line, '='))) {
|
||||||
|
val[0] = 0;
|
||||||
|
val++;
|
||||||
|
|
||||||
|
val_len = strlen(val);
|
||||||
|
if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
|
||||||
|
|
||||||
|
if(!strcmp(line, "PCI_ID")){
|
||||||
|
connection = AudioDeviceConnectionType_PCI;
|
||||||
|
if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
|
||||||
|
WARN("Unexpected input when reading PCI_ID in uevent file.\n");
|
||||||
|
connection = AudioDeviceConnectionType_Unknown;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
|
||||||
|
connection = AudioDeviceConnectionType_USB;
|
||||||
|
else if(!strcmp(line, "PRODUCT"))
|
||||||
|
if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
|
||||||
|
WARN("Unexpected input when reading PRODUCT in uevent file.\n");
|
||||||
|
connection = AudioDeviceConnectionType_Unknown;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fuevent);
|
||||||
|
|
||||||
|
if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
|
||||||
|
UINT serial_number;
|
||||||
|
char buf[128];
|
||||||
|
int len;
|
||||||
|
|
||||||
|
/* As hardly any audio devices have serial numbers, Windows instead
|
||||||
|
appears to use a persistent random number. We emulate this here
|
||||||
|
by instead using the last 8 hex digits of the GUID. */
|
||||||
|
serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
|
||||||
|
|
||||||
|
if(connection == AudioDeviceConnectionType_USB)
|
||||||
|
sprintf(buf, "{1}.USB\\VID_%04X&PID_%04X\\%u&%08X",
|
||||||
|
vendor_id, product_id, device, serial_number);
|
||||||
|
else /* AudioDeviceConnectionType_PCI */
|
||||||
|
sprintf(buf, "{1}.HDAUDIO\\FUNC_01&VEN_%04X&DEV_%04X\\%u&%08X",
|
||||||
|
vendor_id, product_id, device, serial_number);
|
||||||
|
|
||||||
|
len = strlen(buf) + 1;
|
||||||
|
if(*params->buffer_size < len * sizeof(WCHAR)){
|
||||||
|
params->result = E_NOT_SUFFICIENT_BUFFER;
|
||||||
|
*params->buffer_size = len * sizeof(WCHAR);
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
out->vt = VT_LPWSTR;
|
||||||
|
out->pwszVal = params->buffer;
|
||||||
|
ntdll_umbstowcs(buf, len, out->pwszVal, len);
|
||||||
|
params->result = S_OK;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
WARN("Could not open %s for reading\n", uevent);
|
||||||
|
params->result = E_NOTIMPL;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
} else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
|
||||||
|
unsigned int num_speakers, card, device;
|
||||||
|
char hwname[255];
|
||||||
|
|
||||||
|
if (sscanf(name, "plughw:%u,%u", &card, &device))
|
||||||
|
sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
|
||||||
|
else
|
||||||
|
strcpy(hwname, name);
|
||||||
|
|
||||||
|
num_speakers = alsa_probe_num_speakers(hwname);
|
||||||
|
if (num_speakers == 0){
|
||||||
|
params->result = E_FAIL;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
out->vt = VT_UI4;
|
||||||
|
|
||||||
|
if (num_speakers > 6)
|
||||||
|
out->ulVal = KSAUDIO_SPEAKER_STEREO;
|
||||||
|
else if (num_speakers == 6)
|
||||||
|
out->ulVal = KSAUDIO_SPEAKER_5POINT1;
|
||||||
|
else if (num_speakers >= 4)
|
||||||
|
out->ulVal = KSAUDIO_SPEAKER_QUAD;
|
||||||
|
else if (num_speakers >= 2)
|
||||||
|
out->ulVal = KSAUDIO_SPEAKER_STEREO;
|
||||||
|
else if (num_speakers == 1)
|
||||||
|
out->ulVal = KSAUDIO_SPEAKER_MONO;
|
||||||
|
|
||||||
|
params->result = S_OK;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
|
||||||
|
|
||||||
|
params->result = E_NOTIMPL;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
unixlib_entry_t __wine_unix_call_funcs[] =
|
unixlib_entry_t __wine_unix_call_funcs[] =
|
||||||
{
|
{
|
||||||
get_endpoint_ids,
|
get_endpoint_ids,
|
||||||
|
@ -2265,4 +2443,5 @@ unixlib_entry_t __wine_unix_call_funcs[] =
|
||||||
set_volumes,
|
set_volumes,
|
||||||
set_event_handle,
|
set_event_handle,
|
||||||
is_started,
|
is_started,
|
||||||
|
get_prop_value,
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,8 +49,6 @@
|
||||||
#include "audioclient.h"
|
#include "audioclient.h"
|
||||||
#include "audiopolicy.h"
|
#include "audiopolicy.h"
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
|
||||||
|
|
||||||
#include "unixlib.h"
|
#include "unixlib.h"
|
||||||
|
|
||||||
WINE_DEFAULT_DEBUG_CHANNEL(alsa);
|
WINE_DEFAULT_DEBUG_CHANNEL(alsa);
|
||||||
|
@ -2444,58 +2442,12 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned int alsa_probe_num_speakers(char *name) {
|
|
||||||
snd_pcm_t *handle;
|
|
||||||
snd_pcm_hw_params_t *params;
|
|
||||||
int err;
|
|
||||||
unsigned int max_channels = 0;
|
|
||||||
|
|
||||||
if ((err = snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
|
|
||||||
WARN("The device \"%s\" failed to open: %d (%s).\n",
|
|
||||||
name, err, snd_strerror(err));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
params = HeapAlloc(GetProcessHeap(), 0, snd_pcm_hw_params_sizeof());
|
|
||||||
if (!params) {
|
|
||||||
WARN("Out of memory.\n");
|
|
||||||
snd_pcm_close(handle);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_any(handle, params)) < 0) {
|
|
||||||
WARN("snd_pcm_hw_params_any failed for \"%s\": %d (%s).\n",
|
|
||||||
name, err, snd_strerror(err));
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((err = snd_pcm_hw_params_get_channels_max(params,
|
|
||||||
&max_channels)) < 0){
|
|
||||||
WARN("Unable to get max channels: %d (%s)\n", err, snd_strerror(err));
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
|
||||||
HeapFree(GetProcessHeap(), 0, params);
|
|
||||||
snd_pcm_close(handle);
|
|
||||||
|
|
||||||
return max_channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AudioDeviceConnectionType {
|
|
||||||
AudioDeviceConnectionType_Unknown = 0,
|
|
||||||
AudioDeviceConnectionType_PCI,
|
|
||||||
AudioDeviceConnectionType_USB
|
|
||||||
};
|
|
||||||
|
|
||||||
HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
|
HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
|
||||||
{
|
{
|
||||||
|
struct get_prop_value_params params;
|
||||||
char name[256];
|
char name[256];
|
||||||
EDataFlow flow;
|
EDataFlow flow;
|
||||||
|
unsigned int size = 0;
|
||||||
static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
|
|
||||||
{0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
|
|
||||||
};
|
|
||||||
|
|
||||||
TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
|
TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
|
||||||
|
|
||||||
|
@ -2505,116 +2457,27 @@ HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARI
|
||||||
return E_NOINTERFACE;
|
return E_NOINTERFACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsEqualPropertyKey(*prop, devicepath_key))
|
params.alsa_name = name;
|
||||||
{
|
params.flow = flow;
|
||||||
char uevent[MAX_PATH];
|
params.guid = guid;
|
||||||
FILE *fuevent;
|
params.prop = prop;
|
||||||
int card, device;
|
params.value = out;
|
||||||
|
params.buffer = NULL;
|
||||||
|
params.buffer_size = &size;
|
||||||
|
|
||||||
/* only implemented for identifiable devices, i.e. not "default" */
|
while(1) {
|
||||||
if(!sscanf(name, "plughw:%u,%u", &card, &device))
|
ALSA_CALL(get_prop_value, ¶ms);
|
||||||
return E_NOTIMPL;
|
|
||||||
|
|
||||||
sprintf(uevent, "/sys/class/sound/card%u/device/uevent", card);
|
if(params.result != E_NOT_SUFFICIENT_BUFFER)
|
||||||
fuevent = fopen(uevent, "r");
|
break;
|
||||||
|
|
||||||
if(fuevent){
|
CoTaskMemFree(params.buffer);
|
||||||
enum AudioDeviceConnectionType connection = AudioDeviceConnectionType_Unknown;
|
params.buffer = CoTaskMemAlloc(*params.buffer_size);
|
||||||
USHORT vendor_id = 0, product_id = 0;
|
if(!params.buffer)
|
||||||
char line[256];
|
return E_OUTOFMEMORY;
|
||||||
|
|
||||||
while (fgets(line, sizeof(line), fuevent)) {
|
|
||||||
char *val;
|
|
||||||
size_t val_len;
|
|
||||||
|
|
||||||
if((val = strchr(line, '='))) {
|
|
||||||
val[0] = 0;
|
|
||||||
val++;
|
|
||||||
|
|
||||||
val_len = strlen(val);
|
|
||||||
if(val_len > 0 && val[val_len - 1] == '\n') { val[val_len - 1] = 0; }
|
|
||||||
|
|
||||||
if(!strcmp(line, "PCI_ID")){
|
|
||||||
connection = AudioDeviceConnectionType_PCI;
|
|
||||||
if(sscanf(val, "%hX:%hX", &vendor_id, &product_id)<2){
|
|
||||||
WARN("Unexpected input when reading PCI_ID in uevent file.\n");
|
|
||||||
connection = AudioDeviceConnectionType_Unknown;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}else if(!strcmp(line, "DEVTYPE") && !strcmp(val,"usb_interface"))
|
|
||||||
connection = AudioDeviceConnectionType_USB;
|
|
||||||
else if(!strcmp(line, "PRODUCT"))
|
|
||||||
if(sscanf(val, "%hx/%hx/", &vendor_id, &product_id)<2){
|
|
||||||
WARN("Unexpected input when reading PRODUCT in uevent file.\n");
|
|
||||||
connection = AudioDeviceConnectionType_Unknown;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fuevent);
|
|
||||||
|
|
||||||
if(connection == AudioDeviceConnectionType_USB || connection == AudioDeviceConnectionType_PCI){
|
|
||||||
static const WCHAR usbformatW[] = { '{','1','}','.','U','S','B','\\','V','I','D','_',
|
|
||||||
'%','0','4','X','&','P','I','D','_','%','0','4','X','\\',
|
|
||||||
'%','u','&','%','0','8','X',0 }; /* "{1}.USB\VID_%04X&PID_%04X\%u&%08X" */
|
|
||||||
static const WCHAR pciformatW[] = { '{','1','}','.','H','D','A','U','D','I','O','\\','F','U','N','C','_','0','1','&',
|
|
||||||
'V','E','N','_','%','0','4','X','&','D','E','V','_',
|
|
||||||
'%','0','4','X','\\','%','u','&','%','0','8','X',0 }; /* "{1}.HDAUDIO\FUNC_01&VEN_%04X&DEV_%04X\%u&%08X" */
|
|
||||||
UINT serial_number;
|
|
||||||
|
|
||||||
/* As hardly any audio devices have serial numbers, Windows instead
|
|
||||||
appears to use a persistent random number. We emulate this here
|
|
||||||
by instead using the last 8 hex digits of the GUID. */
|
|
||||||
serial_number = (guid->Data4[4] << 24) | (guid->Data4[5] << 16) | (guid->Data4[6] << 8) | guid->Data4[7];
|
|
||||||
|
|
||||||
out->vt = VT_LPWSTR;
|
|
||||||
out->pwszVal = CoTaskMemAlloc(128 * sizeof(WCHAR));
|
|
||||||
|
|
||||||
if(!out->pwszVal)
|
|
||||||
return E_OUTOFMEMORY;
|
|
||||||
|
|
||||||
if(connection == AudioDeviceConnectionType_USB)
|
|
||||||
sprintfW( out->pwszVal, usbformatW, vendor_id, product_id, device, serial_number);
|
|
||||||
else if(connection == AudioDeviceConnectionType_PCI)
|
|
||||||
sprintfW( out->pwszVal, pciformatW, vendor_id, product_id, device, serial_number);
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
WARN("Could not open %s for reading\n", uevent);
|
|
||||||
return E_NOTIMPL;
|
|
||||||
}
|
|
||||||
} else if (flow != eCapture && IsEqualPropertyKey(*prop, PKEY_AudioEndpoint_PhysicalSpeakers)) {
|
|
||||||
unsigned int num_speakers, card, device;
|
|
||||||
char hwname[255];
|
|
||||||
|
|
||||||
if (sscanf(name, "plughw:%u,%u", &card, &device))
|
|
||||||
sprintf(hwname, "hw:%u,%u", card, device); /* must be hw rather than plughw to work */
|
|
||||||
else
|
|
||||||
strcpy(hwname, name);
|
|
||||||
|
|
||||||
num_speakers = alsa_probe_num_speakers(hwname);
|
|
||||||
if (num_speakers == 0)
|
|
||||||
return E_FAIL;
|
|
||||||
|
|
||||||
out->vt = VT_UI4;
|
|
||||||
|
|
||||||
if (num_speakers > 6)
|
|
||||||
out->ulVal = KSAUDIO_SPEAKER_STEREO;
|
|
||||||
else if (num_speakers == 6)
|
|
||||||
out->ulVal = KSAUDIO_SPEAKER_5POINT1;
|
|
||||||
else if (num_speakers >= 4)
|
|
||||||
out->ulVal = KSAUDIO_SPEAKER_QUAD;
|
|
||||||
else if (num_speakers >= 2)
|
|
||||||
out->ulVal = KSAUDIO_SPEAKER_STEREO;
|
|
||||||
else if (num_speakers == 1)
|
|
||||||
out->ulVal = KSAUDIO_SPEAKER_MONO;
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
}
|
||||||
|
if(FAILED(params.result))
|
||||||
|
CoTaskMemFree(params.buffer);
|
||||||
|
|
||||||
TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
|
return params.result;
|
||||||
|
|
||||||
return E_NOTIMPL;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -195,6 +195,18 @@ struct is_started_params
|
||||||
HRESULT result;
|
HRESULT result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct get_prop_value_params
|
||||||
|
{
|
||||||
|
const char *alsa_name;
|
||||||
|
EDataFlow flow;
|
||||||
|
const GUID *guid;
|
||||||
|
const PROPERTYKEY *prop;
|
||||||
|
HRESULT result;
|
||||||
|
PROPVARIANT *value;
|
||||||
|
void *buffer; /* caller allocated buffer to hold value's strings */
|
||||||
|
unsigned int *buffer_size;
|
||||||
|
};
|
||||||
|
|
||||||
enum alsa_funcs
|
enum alsa_funcs
|
||||||
{
|
{
|
||||||
alsa_get_endpoint_ids,
|
alsa_get_endpoint_ids,
|
||||||
|
@ -219,6 +231,7 @@ enum alsa_funcs
|
||||||
alsa_set_volumes,
|
alsa_set_volumes,
|
||||||
alsa_set_event_handle,
|
alsa_set_event_handle,
|
||||||
alsa_is_started,
|
alsa_is_started,
|
||||||
|
alsa_get_prop_value,
|
||||||
};
|
};
|
||||||
|
|
||||||
extern unixlib_handle_t alsa_handle;
|
extern unixlib_handle_t alsa_handle;
|
||||||
|
|
Loading…
Reference in New Issue