Signed-off-by: Huw Davies huw@codeweavers.com --- dlls/winecoreaudio.drv/Makefile.in | 1 + dlls/winecoreaudio.drv/coreaudio.c | 229 ++++++++++++++++++++++++++++- dlls/winecoreaudio.drv/mmdevdrv.c | 203 +++++++------------------ dlls/winecoreaudio.drv/unixlib.h | 44 ++++++ 4 files changed, 322 insertions(+), 155 deletions(-) create mode 100644 dlls/winecoreaudio.drv/unixlib.h
diff --git a/dlls/winecoreaudio.drv/Makefile.in b/dlls/winecoreaudio.drv/Makefile.in index afbc50c7148..b6bf5bd4587 100644 --- a/dlls/winecoreaudio.drv/Makefile.in +++ b/dlls/winecoreaudio.drv/Makefile.in @@ -1,4 +1,5 @@ MODULE = winecoreaudio.drv +UNIXLIB = winecoreaudio.so IMPORTS = uuid ole32 user32 advapi32 DELAYIMPORTS = winmm EXTRALIBS = $(COREAUDIO_LIBS) diff --git a/dlls/winecoreaudio.drv/coreaudio.c b/dlls/winecoreaudio.drv/coreaudio.c index 1f222367bed..f3af24f80fb 100644 --- a/dlls/winecoreaudio.drv/coreaudio.c +++ b/dlls/winecoreaudio.drv/coreaudio.c @@ -1,5 +1,8 @@ /* - * Wine Driver for CoreAudio + * Unixlib for winecoreaudio driver. + * + * Copyright 2011 Andrew Eikum for CodeWeavers + * Copyright 2021 Huw Davies * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -15,16 +18,230 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ - +#if 0 +#pragma makedep unix +#endif
#include "config.h"
+#define LoadResource __carbon_LoadResource +#define CompareString __carbon_CompareString +#define GetCurrentThread __carbon_GetCurrentThread +#define GetCurrentProcess __carbon_GetCurrentProcess + #include <stdarg.h>
+#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <fenv.h> +#include <unistd.h> + +#include <libkern/OSAtomic.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioToolbox/AudioFormat.h> +#include <AudioToolbox/AudioConverter.h> +#include <AudioUnit/AudioUnit.h> + +#undef LoadResource +#undef CompareString +#undef GetCurrentThread +#undef GetCurrentProcess +#undef _CDECL + +#include "ntstatus.h" +#define WIN32_NO_STATUS #include "windef.h" #include "winbase.h" -#include "wingdi.h" -#include "winuser.h" -#include "mmddk.h" -#include "coreaudio.h" +#include "winnls.h" +#include "winreg.h" +#include "mmdeviceapi.h" +#include "initguid.h" +#include "audioclient.h" #include "wine/debug.h" +#include "wine/unicode.h" +#include "wine/unixlib.h" + +#include "unixlib.h" + +WINE_DEFAULT_DEBUG_CHANNEL(coreaudio); + +static HRESULT osstatus_to_hresult(OSStatus sc) +{ + switch(sc){ + case kAudioFormatUnsupportedDataFormatError: + case kAudioFormatUnknownFormatError: + case kAudioDeviceUnsupportedFormatError: + return AUDCLNT_E_UNSUPPORTED_FORMAT; + case kAudioHardwareBadDeviceError: + return AUDCLNT_E_DEVICE_INVALIDATED; + } + return E_FAIL; +} + +static AudioObjectPropertyScope get_scope(EDataFlow flow) +{ + return (flow == eRender) ? kAudioDevicePropertyScopeOutput : kAudioDevicePropertyScopeInput; +} + +static BOOL device_has_channels(AudioDeviceID device, EDataFlow flow) +{ + AudioObjectPropertyAddress addr; + AudioBufferList *buffers; + BOOL ret = FALSE; + OSStatus sc; + UInt32 size; + int i; + + addr.mSelector = kAudioDevicePropertyStreamConfiguration; + addr.mScope = get_scope(flow); + addr.mElement = 0; + + sc = AudioObjectGetPropertyDataSize(device, &addr, 0, NULL, &size); + if(sc != noErr){ + WARN("Unable to get _StreamConfiguration property size for device %u: %x\n", + (unsigned int)device, (int)sc); + return FALSE; + } + + buffers = malloc(size); + if(!buffers) return FALSE; + + sc = AudioObjectGetPropertyData(device, &addr, 0, NULL, &size, buffers); + if(sc != noErr){ + WARN("Unable to get _StreamConfiguration property for device %u: %x\n", + (unsigned int)device, (int)sc); + free(buffers); + return FALSE; + } + + for(i = 0; i < buffers->mNumberBuffers; i++){ + if(buffers->mBuffers[i].mNumberChannels > 0){ + ret = TRUE; + break; + } + } + free(buffers); + return ret; +} + +static NTSTATUS get_endpoint_ids(void *args) +{ + struct get_endpoint_ids_params *params = args; + unsigned int num_devices, i, needed; + AudioDeviceID *devices, default_id; + AudioObjectPropertyAddress addr; + struct endpoint *endpoint; + UInt32 devsize, size; + struct endpoint_info + { + CFStringRef name; + AudioDeviceID id; + } *info; + OSStatus sc; + WCHAR *ptr; + + params->num = 0; + params->default_idx = 0; + + addr.mScope = kAudioObjectPropertyScopeGlobal; + addr.mElement = kAudioObjectPropertyElementMaster; + if(params->flow == eRender) addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; + else if(params->flow == eCapture) addr.mSelector = kAudioHardwarePropertyDefaultInputDevice; + else{ + params->result = E_INVALIDARG; + return STATUS_SUCCESS; + } + + size = sizeof(default_id); + sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &size, &default_id); + if(sc != noErr){ + WARN("Getting _DefaultInputDevice property failed: %x\n", (int)sc); + default_id = -1; + } + + addr.mSelector = kAudioHardwarePropertyDevices; + sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, NULL, &devsize); + if(sc != noErr){ + WARN("Getting _Devices property size failed: %x\n", (int)sc); + params->result = osstatus_to_hresult(sc); + return STATUS_SUCCESS; + } + + num_devices = devsize / sizeof(AudioDeviceID); + devices = malloc(devsize); + info = malloc(num_devices * sizeof(*info)); + if(!devices || !info){ + free(info); + free(devices); + params->result = E_OUTOFMEMORY; + return STATUS_SUCCESS; + } + + sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, &devsize, devices); + if(sc != noErr){ + WARN("Getting _Devices property failed: %x\n", (int)sc); + free(info); + free(devices); + params->result = osstatus_to_hresult(sc); + return STATUS_SUCCESS; + } + + addr.mSelector = kAudioObjectPropertyName; + addr.mScope = get_scope(params->flow); + addr.mElement = 0; + + for(i = 0; i < num_devices; i++){ + if(!device_has_channels(devices[i], params->flow)) continue; + + size = sizeof(CFStringRef); + sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL, &size, &info[params->num].name); + if(sc != noErr){ + WARN("Unable to get _Name property for device %u: %x\n", + (unsigned int)devices[i], (int)sc); + continue; + } + info[params->num++].id = devices[i]; + } + free(devices); + + needed = sizeof(*endpoint) * params->num; + endpoint = params->endpoints; + ptr = (WCHAR *)(endpoint + params->num); + + for(i = 0; i < params->num; i++){ + SIZE_T len = CFStringGetLength(info[i].name); + needed += (len + 1) * sizeof(WCHAR); + + if(needed <= params->size){ + endpoint->name = ptr; + CFStringGetCharacters(info[i].name, CFRangeMake(0, len), (UniChar*)endpoint->name); + ptr[len] = 0; + endpoint->id = info[i].id; + endpoint++; + ptr += len + 1; + } + CFRelease(info[i].name); + if(info[i].id == default_id) params->default_idx = i; + } + free(info); + + if(needed > params->size){ + params->size = needed; + params->result = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + else params->result = S_OK; + + return STATUS_SUCCESS; +} + +unixlib_entry_t __wine_unix_call_funcs[] = +{ + get_endpoint_ids, +}; diff --git a/dlls/winecoreaudio.drv/mmdevdrv.c b/dlls/winecoreaudio.drv/mmdevdrv.c index f07f4bae5fb..f8b538971f2 100644 --- a/dlls/winecoreaudio.drv/mmdevdrv.c +++ b/dlls/winecoreaudio.drv/mmdevdrv.c @@ -56,8 +56,10 @@ #include "winnls.h" #include "winreg.h" #include "wine/debug.h" +#include "wine/heap.h" #include "wine/unicode.h" #include "wine/list.h" +#include "wine/unixlib.h"
#include "ole2.h" #include "mmdeviceapi.h" @@ -69,9 +71,12 @@ #include "endpointvolume.h" #include "audioclient.h" #include "audiopolicy.h" +#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
+unixlib_handle_t coreaudio_handle = 0; + #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
static const REFERENCE_TIME DefaultPeriod = 100000; @@ -245,6 +250,9 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) switch (reason) { case DLL_PROCESS_ATTACH: + if(NtQueryVirtualMemory(GetCurrentProcess(), dll, MemoryWineUnixFuncs, + &coreaudio_handle, sizeof(coreaudio_handle), NULL)) + return FALSE; g_timer_q = CreateTimerQueue(); if(!g_timer_q) return FALSE; @@ -319,7 +327,7 @@ exit: RegCloseKey(drv_key); }
-static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid) +static void get_device_guid(EDataFlow flow, DWORD device_id, GUID *guid) { HKEY key = NULL, dev_key; DWORD type, size = sizeof(*guid); @@ -333,7 +341,7 @@ static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid) key_name[0] = '0'; key_name[1] = ',';
- sprintfW(key_name + 2, key_fmt, device); + sprintfW(key_name + 2, key_fmt, device_id);
if(RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_WRITE|KEY_READ, &key) == ERROR_SUCCESS){ if(RegOpenKeyExW(key, key_name, 0, KEY_READ, &dev_key) == ERROR_SUCCESS){ @@ -359,164 +367,61 @@ static void get_device_guid(EDataFlow flow, AudioDeviceID device, GUID *guid) RegCloseKey(key); }
-HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids, - GUID **guids, UINT *num, UINT *def_index) +HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, + GUID **guids_out, UINT *num, UINT *def_index) { - UInt32 devsize, size; - AudioDeviceID *devices; - AudioDeviceID default_id; - AudioObjectPropertyAddress addr; - OSStatus sc; - int i, ndevices; - - TRACE("%d %p %p %p\n", flow, ids, num, def_index); - - addr.mScope = kAudioObjectPropertyScopeGlobal; - addr.mElement = kAudioObjectPropertyElementMaster; - if(flow == eRender) - addr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - else if(flow == eCapture) - addr.mSelector = kAudioHardwarePropertyDefaultInputDevice; - else - return E_INVALIDARG; - - size = sizeof(default_id); - sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, - NULL, &size, &default_id); - if(sc != noErr){ - WARN("Getting _DefaultInputDevice property failed: %x\n", (int)sc); - default_id = -1; - } - - addr.mSelector = kAudioHardwarePropertyDevices; - sc = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &addr, 0, - NULL, &devsize); - if(sc != noErr){ - WARN("Getting _Devices property size failed: %x\n", (int)sc); - return osstatus_to_hresult(sc); - } + struct get_endpoint_ids_params params; + unsigned int i; + GUID *guids; + WCHAR **ids;
- devices = HeapAlloc(GetProcessHeap(), 0, devsize); - if(!devices) - return E_OUTOFMEMORY; - - sc = AudioObjectGetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL, - &devsize, devices); - if(sc != noErr){ - WARN("Getting _Devices property failed: %x\n", (int)sc); - HeapFree(GetProcessHeap(), 0, devices); - return osstatus_to_hresult(sc); - } + TRACE("%d %p %p %p\n", flow, ids_out, num, def_index);
- ndevices = devsize / sizeof(AudioDeviceID); + params.flow = flow; + params.size = 1000; + params.endpoints = NULL; + do{ + heap_free(params.endpoints); + params.endpoints = heap_alloc(params.size); + UNIX_CALL(get_endpoint_ids, ¶ms); + }while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
- *ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *)); - if(!*ids){ - HeapFree(GetProcessHeap(), 0, devices); - return E_OUTOFMEMORY; - } + if(FAILED(params.result)) goto end;
- *guids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(GUID)); - if(!*guids){ - HeapFree(GetProcessHeap(), 0, *ids); - HeapFree(GetProcessHeap(), 0, devices); - return E_OUTOFMEMORY; + ids = heap_alloc_zero(params.num * sizeof(*ids)); + guids = heap_alloc(params.num * sizeof(*guids)); + if(!ids || !guids){ + params.result = E_OUTOFMEMORY; + goto end; }
- *num = 0; - *def_index = (UINT)-1; - for(i = 0; i < ndevices; ++i){ - AudioBufferList *buffers; - CFStringRef name; - SIZE_T len; - int j; - - addr.mSelector = kAudioDevicePropertyStreamConfiguration; - if(flow == eRender) - addr.mScope = kAudioDevicePropertyScopeOutput; - else - addr.mScope = kAudioDevicePropertyScopeInput; - addr.mElement = 0; - sc = AudioObjectGetPropertyDataSize(devices[i], &addr, 0, NULL, &size); - if(sc != noErr){ - WARN("Unable to get _StreamConfiguration property size for " - "device %u: %x\n", (unsigned int)devices[i], (int)sc); - continue; - } - - buffers = HeapAlloc(GetProcessHeap(), 0, size); - if(!buffers){ - HeapFree(GetProcessHeap(), 0, devices); - for(j = 0; j < *num; ++j) - HeapFree(GetProcessHeap(), 0, (*ids)[j]); - HeapFree(GetProcessHeap(), 0, *guids); - HeapFree(GetProcessHeap(), 0, *ids); - return E_OUTOFMEMORY; - } - - sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL, - &size, buffers); - if(sc != noErr){ - WARN("Unable to get _StreamConfiguration property for " - "device %u: %x\n", (unsigned int)devices[i], (int)sc); - HeapFree(GetProcessHeap(), 0, buffers); - continue; - } - - /* check that there's at least one channel in this device before - * we claim it as usable */ - for(j = 0; j < buffers->mNumberBuffers; ++j) - if(buffers->mBuffers[j].mNumberChannels > 0) - break; - if(j >= buffers->mNumberBuffers){ - HeapFree(GetProcessHeap(), 0, buffers); - continue; - } - - HeapFree(GetProcessHeap(), 0, buffers); - - size = sizeof(name); - addr.mSelector = kAudioObjectPropertyName; - sc = AudioObjectGetPropertyData(devices[i], &addr, 0, NULL, - &size, &name); - if(sc != noErr){ - WARN("Unable to get _Name property for device %u: %x\n", - (unsigned int)devices[i], (int)sc); - continue; + for(i = 0; i < params.num; i++){ + int size = (strlenW(params.endpoints[i].name) + 1) * sizeof(WCHAR); + ids[i] = heap_alloc(size); + if(!ids[i]){ + params.result = E_OUTOFMEMORY; + goto end; } - - len = CFStringGetLength(name) + 1; - (*ids)[*num] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); - if(!(*ids)[*num]){ - CFRelease(name); - HeapFree(GetProcessHeap(), 0, devices); - for(j = 0; j < *num; ++j) - HeapFree(GetProcessHeap(), 0, (*ids)[j]); - HeapFree(GetProcessHeap(), 0, *ids); - HeapFree(GetProcessHeap(), 0, *guids); - return E_OUTOFMEMORY; + memcpy(ids[i], params.endpoints[i].name, size); + get_device_guid(flow, params.endpoints[i].id, guids + i); + } + *def_index = params.default_idx; + +end: + heap_free(params.endpoints); + if(FAILED(params.result)){ + heap_free(guids); + if(ids){ + for(i = 0; i < params.num; i++) heap_free(ids[i]); + heap_free(ids); } - CFStringGetCharacters(name, CFRangeMake(0, len - 1), (UniChar*)(*ids)[*num]); - ((*ids)[*num])[len - 1] = 0; - CFRelease(name); - - get_device_guid(flow, devices[i], &(*guids)[*num]); - - if(*def_index == (UINT)-1 && devices[i] == default_id) - *def_index = *num; - - TRACE("device %u: id %s key %u%s\n", *num, debugstr_w((*ids)[*num]), - (unsigned int)devices[i], (*def_index == *num) ? " (default)" : ""); - - (*num)++; + }else{ + *ids_out = ids; + *guids_out = guids; + *num = params.num; }
- if(*def_index == (UINT)-1) - *def_index = 0; - - HeapFree(GetProcessHeap(), 0, devices); - - return S_OK; + return params.result; }
static BOOL get_deviceid_by_guid(GUID *guid, AudioDeviceID *id, EDataFlow *flow) diff --git a/dlls/winecoreaudio.drv/unixlib.h b/dlls/winecoreaudio.drv/unixlib.h new file mode 100644 index 00000000000..7c1200464b9 --- /dev/null +++ b/dlls/winecoreaudio.drv/unixlib.h @@ -0,0 +1,44 @@ +/* + * Unixlib header file for winecoreaudio driver. + * + * Copyright 2021 Huw Davies + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +struct endpoint +{ + WCHAR *name; + DWORD id; +}; + +struct get_endpoint_ids_params +{ + EDataFlow flow; + struct endpoint *endpoints; + unsigned int size; + HRESULT result; + unsigned int num; + unsigned int default_idx; +}; + +enum unix_funcs +{ + unix_get_endpoint_ids, +}; + +extern unixlib_handle_t coreaudio_handle; + +#define UNIX_CALL( func, params ) __wine_unix_call( coreaudio_handle, unix_ ## func, params )