winecoreaudio: Move get_endpoint_ids to a 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:
Huw Davies 2021-11-17 09:08:49 -06:00 committed by Alexandre Julliard
parent 0e52d77f92
commit c84923b200
4 changed files with 325 additions and 158 deletions

View file

@ -1,4 +1,5 @@
MODULE = winecoreaudio.drv
UNIXLIB = winecoreaudio.so
IMPORTS = uuid ole32 user32 advapi32
DELAYIMPORTS = winmm
EXTRALIBS = $(COREAUDIO_LIBS)

View file

@ -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,
};

View file

@ -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;
struct get_endpoint_ids_params params;
unsigned int i;
GUID *guids;
WCHAR **ids;
TRACE("%d %p %p %p\n", flow, ids, num, def_index);
TRACE("%d %p %p %p\n", flow, ids_out, 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;
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, &params);
}while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
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;
if(FAILED(params.result)) goto end;
ids = heap_alloc_zero(params.num * sizeof(*ids));
guids = heap_alloc(params.num * sizeof(*guids));
if(!ids || !guids){
params.result = E_OUTOFMEMORY;
goto end;
}
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);
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;
}
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);
}
}else{
*ids_out = ids;
*guids_out = guids;
*num = params.num;
}
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);
}
ndevices = devsize / sizeof(AudioDeviceID);
*ids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(WCHAR *));
if(!*ids){
HeapFree(GetProcessHeap(), 0, devices);
return E_OUTOFMEMORY;
}
*guids = HeapAlloc(GetProcessHeap(), 0, ndevices * sizeof(GUID));
if(!*guids){
HeapFree(GetProcessHeap(), 0, *ids);
HeapFree(GetProcessHeap(), 0, devices);
return E_OUTOFMEMORY;
}
*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;
}
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;
}
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)++;
}
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)

View file

@ -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 )