Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/winealsa.drv/alsa.c | 181 ++++++++++++++++++++++++++++++++++- dlls/winealsa.drv/mmdevdrv.c | 177 ++++------------------------------ dlls/winealsa.drv/unixlib.h | 13 +++ 3 files changed, 213 insertions(+), 158 deletions(-)
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c index c199a2e603d..085066622c0 100644 --- a/dlls/winealsa.drv/alsa.c +++ b/dlls/winealsa.drv/alsa.c @@ -34,13 +34,13 @@ #include "windef.h" #include "winbase.h" #include "winternl.h" +#include "initguid.h" #include "mmdeviceapi.h"
#include "wine/debug.h" #include "wine/list.h" #include "wine/unixlib.h"
-#include "initguid.h" #include "unixlib.h"
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); }
+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[] = { get_endpoint_ids, @@ -2265,4 +2443,5 @@ unixlib_entry_t __wine_unix_call_funcs[] = set_volumes, set_event_handle, is_started, + get_prop_value, }; diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c index c2ec3dd54af..389c40b47de 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -49,8 +49,6 @@ #include "audioclient.h" #include "audiopolicy.h"
-#include <alsa/asoundlib.h> - #include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(alsa); @@ -2444,58 +2442,12 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, 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) { + struct get_prop_value_params params; char name[256]; EDataFlow flow; - - static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */ - {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2 - }; + unsigned int size = 0;
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; }
- 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)) - return E_NOTIMPL; - - 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){ - 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; + params.alsa_name = name; + params.flow = flow; + params.guid = guid; + params.prop = prop; + params.value = out; + params.buffer = NULL; + params.buffer_size = &size;
- out->vt = VT_UI4; + while(1) { + ALSA_CALL(get_prop_value, ¶ms);
- 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; + if(params.result != E_NOT_SUFFICIENT_BUFFER) + break;
- return S_OK; + CoTaskMemFree(params.buffer); + params.buffer = CoTaskMemAlloc(*params.buffer_size); + if(!params.buffer) + return E_OUTOFMEMORY; } + if(FAILED(params.result)) + CoTaskMemFree(params.buffer);
- TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid); - - return E_NOTIMPL; + return params.result; } diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h index 4326f79a50b..c2de48cef65 100644 --- a/dlls/winealsa.drv/unixlib.h +++ b/dlls/winealsa.drv/unixlib.h @@ -195,6 +195,18 @@ struct is_started_params 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 { alsa_get_endpoint_ids, @@ -219,6 +231,7 @@ enum alsa_funcs alsa_set_volumes, alsa_set_event_handle, alsa_is_started, + alsa_get_prop_value, };
extern unixlib_handle_t alsa_handle;
Signed-off-by: Andrew Eikum aeikum@codeweavers.com
On Mon, Mar 14, 2022 at 10:21:46AM +0000, Huw Davies wrote:
Signed-off-by: Huw Davies huw@codeweavers.com
dlls/winealsa.drv/alsa.c | 181 ++++++++++++++++++++++++++++++++++- dlls/winealsa.drv/mmdevdrv.c | 177 ++++------------------------------ dlls/winealsa.drv/unixlib.h | 13 +++ 3 files changed, 213 insertions(+), 158 deletions(-)
diff --git a/dlls/winealsa.drv/alsa.c b/dlls/winealsa.drv/alsa.c index c199a2e603d..085066622c0 100644 --- a/dlls/winealsa.drv/alsa.c +++ b/dlls/winealsa.drv/alsa.c @@ -34,13 +34,13 @@ #include "windef.h" #include "winbase.h" #include "winternl.h" +#include "initguid.h" #include "mmdeviceapi.h"
#include "wine/debug.h" #include "wine/list.h" #include "wine/unixlib.h"
-#include "initguid.h" #include "unixlib.h"
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); }
+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[] = { get_endpoint_ids, @@ -2265,4 +2443,5 @@ unixlib_entry_t __wine_unix_call_funcs[] = set_volumes, set_event_handle, is_started,
- get_prop_value,
}; diff --git a/dlls/winealsa.drv/mmdevdrv.c b/dlls/winealsa.drv/mmdevdrv.c index c2ec3dd54af..389c40b47de 100644 --- a/dlls/winealsa.drv/mmdevdrv.c +++ b/dlls/winealsa.drv/mmdevdrv.c @@ -49,8 +49,6 @@ #include "audioclient.h" #include "audiopolicy.h"
-#include <alsa/asoundlib.h>
#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(alsa); @@ -2444,58 +2442,12 @@ HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, 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) {
- struct get_prop_value_params params; char name[256]; EDataFlow flow;
- static const PROPERTYKEY devicepath_key = { /* undocumented? - {b3f8fa53-0004-438e-9003-51a46e139bfc},2 */
{0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
- };
unsigned int size = 0;
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; }
- 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))
return E_NOTIMPL;
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){
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;
- params.alsa_name = name;
- params.flow = flow;
- params.guid = guid;
- params.prop = prop;
- params.value = out;
- params.buffer = NULL;
- params.buffer_size = &size;
out->vt = VT_UI4;
- while(1) {
ALSA_CALL(get_prop_value, ¶ms);
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;
if(params.result != E_NOT_SUFFICIENT_BUFFER)
break;
return S_OK;
CoTaskMemFree(params.buffer);
params.buffer = CoTaskMemAlloc(*params.buffer_size);
if(!params.buffer)
}return E_OUTOFMEMORY;
- if(FAILED(params.result))
CoTaskMemFree(params.buffer);
- TRACE("Unimplemented property %s,%u\n", wine_dbgstr_guid(&prop->fmtid), prop->pid);
- return E_NOTIMPL;
- return params.result;
} diff --git a/dlls/winealsa.drv/unixlib.h b/dlls/winealsa.drv/unixlib.h index 4326f79a50b..c2de48cef65 100644 --- a/dlls/winealsa.drv/unixlib.h +++ b/dlls/winealsa.drv/unixlib.h @@ -195,6 +195,18 @@ struct is_started_params 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 { alsa_get_endpoint_ids, @@ -219,6 +231,7 @@ enum alsa_funcs alsa_set_volumes, alsa_set_event_handle, alsa_is_started,
- alsa_get_prop_value,
};
extern unixlib_handle_t alsa_handle;
2.25.1
Greetings Huw,
Been following your winealsa work and I'm pretty hyped about this commit - mmdev is finally alsa-free \o/
Have you considered splitting the props handling - get_device_path, get_physical_speakers? AFAICT this should reduce the duplication on the unix side (alsa vs pulse vs others). More importantly it makes it easier to avoid the caller allocated string, by passing the vendor/device/other data directly.
What are your thoughts on having a single mmdev.drv, which calls into winealsa.drv/winepulse.drv/other? Are wineandroid/mmdev, winecoreaudio and wineoss in scope in the mid/long run?
Thanks in advance, Emil
On Tue, Mar 15, 2022 at 08:38:32AM +0000, Emil Velikov wrote:
Have you considered splitting the props handling - get_device_path, get_physical_speakers? AFAICT this should reduce the duplication on the unix side (alsa vs pulse vs others). More importantly it makes it easier to avoid the caller allocated string, by passing the vendor/device/other data directly.
We could split these, but I didn't want the number of syscalls to escalate. If there really are only two properties then that doesn't sound too bad.
What are your thoughts on having a single mmdev.drv, which calls into winealsa.drv/winepulse.drv/other? Are wineandroid/mmdev, winecoreaudio and wineoss in scope in the mid/long run?
Yes, long term the aim would be to have a single PE-side. I've been keeping the unixlib interfaces as close as possible to facilitate this. Once all of the drivers are done, we'll had a good idea of what that'll look like.
winecoreaudio is already done and I've started on wineoss, though I still have the midi bits of winealsa to upstream.
Until you'd mentioned it, I'd forgotten about sound side of wineandroid, so I guess that's one more to do :-(
Huw.
On Tue, 15 Mar 2022 at 08:56, Huw Davies huw@codeweavers.com wrote:
On Tue, Mar 15, 2022 at 08:38:32AM +0000, Emil Velikov wrote:
Have you considered splitting the props handling - get_device_path, get_physical_speakers? AFAICT this should reduce the duplication on the unix side (alsa vs pulse vs others). More importantly it makes it easier to avoid the caller allocated string, by passing the vendor/device/other data directly.
We could split these, but I didn't want the number of syscalls to escalate. If there really are only two properties then that doesn't sound too bad.
Are there any technical implications of having say 10 vs 20 syscalls or is it mostly to preserve one's sanity? The syscall lookup machinery should not care, unless we're talking about orders of magnitude.
What are your thoughts on having a single mmdev.drv, which calls into winealsa.drv/winepulse.drv/other? Are wineandroid/mmdev, winecoreaudio and wineoss in scope in the mid/long run?
Yes, long term the aim would be to have a single PE-side. I've been keeping the unixlib interfaces as close as possible to facilitate this. Once all of the drivers are done, we'll had a good idea of what that'll look like.
winecoreaudio is already done and I've started on wineoss, though I still have the midi bits of winealsa to upstream.
Very nice.
Speaking of midi bits: Which version of Windows deprecates(?) winmm.dll and/or the multimedia extensions MME?
Until you'd mentioned it, I'd forgotten about sound side of wineandroid, so I guess that's one more to do :-(
^W^W^W^W^W I didn't say anything :-P
Keep up the good work. -Emil
On Tue, Mar 15, 2022 at 09:39:35AM +0000, Emil Velikov wrote:
On Tue, 15 Mar 2022 at 08:56, Huw Davies huw@codeweavers.com wrote:
On Tue, Mar 15, 2022 at 08:38:32AM +0000, Emil Velikov wrote:
Have you considered splitting the props handling - get_device_path, get_physical_speakers? AFAICT this should reduce the duplication on the unix side (alsa vs pulse vs others). More importantly it makes it easier to avoid the caller allocated string, by passing the vendor/device/other data directly.
We could split these, but I didn't want the number of syscalls to escalate. If there really are only two properties then that doesn't sound too bad.
Are there any technical implications of having say 10 vs 20 syscalls or is it mostly to preserve one's sanity? The syscall lookup machinery should not care, unless we're talking about orders of magnitude.
No technical reasons, just trying to keep the api sane.
Until you'd mentioned it, I'd forgotten about sound side of wineandroid, so I guess that's one more to do :-(
^W^W^W^W^W I didn't say anything :-P
It does beg the question of whether wineandroid is still useful for people.
Huw.
Huw Davies huw@codeweavers.com writes:
On Tue, Mar 15, 2022 at 09:39:35AM +0000, Emil Velikov wrote:
On Tue, 15 Mar 2022 at 08:56, Huw Davies huw@codeweavers.com wrote:
Until you'd mentioned it, I'd forgotten about sound side of wineandroid, so I guess that's one more to do :-(
^W^W^W^W^W I didn't say anything :-P
It does beg the question of whether wineandroid is still useful for people.
Sadly it's mostly bitrotting at this point. Unless someone wants to work on bringing it up to date with latest Android, it's not worth spending a lot of effort on it.