diff options
-rw-r--r-- | Alc/backends/mmdevapi.c | 292 | ||||
-rw-r--r-- | Alc/helpers.c | 8 |
2 files changed, 289 insertions, 11 deletions
diff --git a/Alc/backends/mmdevapi.c b/Alc/backends/mmdevapi.c index e109b5b5..471fc78d 100644 --- a/Alc/backends/mmdevapi.c +++ b/Alc/backends/mmdevapi.c @@ -29,6 +29,9 @@ #include <audioclient.h> #include <cguid.h> #include <mmreg.h> +#include <propsys.h> +#include <propkey.h> +#include <devpkey.h> #ifndef _WAVEFORMATEXTENSIBLE_ #include <ks.h> #include <ksmedia.h> @@ -52,6 +55,7 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0 typedef struct { + GUID guid; IMMDevice *mmdev; IAudioClient *client; HANDLE hNotifyEvent; @@ -63,7 +67,16 @@ typedef struct { } MMDevApiData; +typedef struct { + ALCchar *name; + GUID guid; +} DevMap; + static const ALCchar mmDevice[] = "WASAPI Default"; +static DevMap *PlaybackDeviceList; +static ALuint NumPlaybackDevices; +static DevMap *CaptureDeviceList; +static ALuint NumCaptureDevices; static HANDLE ThreadHdl; @@ -78,6 +91,7 @@ typedef struct { #define WM_USER_ResetDevice (WM_USER+1) #define WM_USER_StopDevice (WM_USER+2) #define WM_USER_CloseDevice (WM_USER+3) +#define WM_USER_Enumerate (WM_USER+4) static HRESULT WaitForResponse(ThreadRequest *req) { @@ -88,6 +102,165 @@ static HRESULT WaitForResponse(ThreadRequest *req) } +static HRESULT get_mmdevice_by_guid(IMMDeviceEnumerator *devenum, EDataFlow flowdir, const GUID *guid, IMMDevice **out) +{ + IMMDeviceCollection *coll; + DWORD count, i; + HRESULT hr; + + hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll); + if(FAILED(hr)) + { + ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); + return hr; + } + + count = 0; + IMMDeviceCollection_GetCount(coll, &count); + for(i = 0;i < count;++i) + { + IMMDevice *device; + IPropertyStore *ps; + PROPVARIANT pv; + GUID devid; + + if(FAILED(IMMDeviceCollection_Item(coll, i, &device))) + continue; + + hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); + if(FAILED(hr)) + { + WARN("OpenPropertyStore failed: 0x%08lx\n", hr); + continue; + } + + PropVariantInit(&pv); + + hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pv); + if(FAILED(hr)) + { + PropVariantClear(&pv); + IPropertyStore_Release(ps); + WARN("GetValue failed: 0x%08lx\n", hr); + continue; + } + CLSIDFromString(pv.pwszVal, &devid); + + PropVariantClear(&pv); + IPropertyStore_Release(ps); + + if(IsEqualGUID(&devid, guid)) + { + *out = device; + break; + } + + IMMDevice_Release(device); + } + hr = ((i==count) ? E_FAIL : S_OK); + + IMMDeviceCollection_Release(coll); + return hr; +} + + +static void add_device(IMMDevice *device, DevMap *devmap) +{ + IPropertyStore *ps; + PROPVARIANT pvguid; + PROPVARIANT pvname; + HRESULT hr; + int len; + + hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); + if(FAILED(hr)) + { + WARN("OpenPropertyStore failed: 0x%08lx\n", hr); + return; + } + + PropVariantInit(&pvguid); + PropVariantInit(&pvname); + + hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pvguid); + if(FAILED(hr)) + WARN("GetValue failed: 0x%08lx\n", hr); + else + { + hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname); + if(FAILED(hr)) + WARN("GetValue failed: 0x%08lx\n", hr); + } + if(SUCCEEDED(hr)) + { + CLSIDFromString(pvguid.pwszVal, &devmap->guid); + if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0) + { + devmap->name = calloc(1, len); + WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, devmap->name, len, NULL, NULL); + } + } + + PropVariantClear(&pvname); + PropVariantClear(&pvguid); + IPropertyStore_Release(ps); +} + +static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs) +{ + IMMDeviceCollection *coll; + IMMDevice *defdev = NULL; + DevMap *devlist; + DWORD count; + HRESULT hr; + DWORD idx; + DWORD i; + + hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll); + if(FAILED(hr)) + { + ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr); + return NULL; + } + + count = 0; + hr = IMMDeviceCollection_GetCount(coll, &count); + if(SUCCEEDED(hr) && count > 0) + { + devlist = calloc(count, sizeof(*devlist)); + if(!devlist) + { + IMMDeviceCollection_Release(coll); + return NULL; + } + + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir, + eMultimedia, &defdev); + } + if(SUCCEEDED(hr) && defdev != NULL) + add_device(defdev, &devlist[idx++]); + + for(i = 0;i < count && idx < count;++i) + { + IMMDevice *device; + + if(FAILED(IMMDeviceCollection_Item(coll, i, &device))) + continue; + + if(device != defdev) + add_device(device, &devlist[idx++]); + + IMMDevice_Release(device); + } + + if(defdev) IMMDevice_Release(defdev); + IMMDeviceCollection_Release(coll); + + *numdevs = idx; + return devlist; +} + + static ALuint MMDevApiProc(ALvoid *ptr) { ALCdevice *device = ptr; @@ -495,16 +668,18 @@ static DWORD CALLBACK MMDevApiMsgProc(void *ptr) device = (ALCdevice*)msg.lParam; data = device->ExtraData; - cohr = S_OK; - hr = E_FAIL; + hr = cohr = S_OK; if(++deviceCount == 1) - cohr = CoInitialize(NULL); - if(SUCCEEDED(cohr)) + hr = cohr = CoInitialize(NULL); + if(SUCCEEDED(hr)) hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); if(SUCCEEDED(hr)) { Enumerator = ptr; - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); + if(IsEqualGUID(&data->guid, &GUID_NULL)) + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); + else + hr = get_mmdevice_by_guid(Enumerator, eRender, &data->guid, &data->mmdev); IMMDeviceEnumerator_Release(Enumerator); Enumerator = NULL; } @@ -572,6 +747,54 @@ static DWORD CALLBACK MMDevApiMsgProc(void *ptr) SetEvent(req->FinishedEvt); continue; + case WM_USER_Enumerate: + req = (ThreadRequest*)msg.wParam; + + hr = cohr = S_OK; + if(++deviceCount == 1) + hr = cohr = CoInitialize(NULL); + if(SUCCEEDED(hr)) + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + EDataFlow flowdir; + DevMap **devlist; + ALuint *numdevs; + ALuint i; + + Enumerator = ptr; + if(msg.lParam == CAPTURE_DEVICE_PROBE) + { + flowdir = eCapture; + devlist = &CaptureDeviceList; + numdevs = &NumCaptureDevices; + } + else + { + flowdir = eRender; + devlist = &PlaybackDeviceList; + numdevs = &NumPlaybackDevices; + } + + for(i = 0;i < *numdevs;i++) + free((*devlist)[i].name); + free(*devlist); + *devlist = NULL; + *numdevs = 0; + + *devlist = ProbeDevices(Enumerator, flowdir, numdevs); + + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + + if(--deviceCount == 0 && SUCCEEDED(cohr)) + CoUninitialize(); + + req->result = S_OK; + SetEvent(req->FinishedEvt); + continue; + default: ERR("Unexpected message: %u\n", msg.message); continue; @@ -611,11 +834,6 @@ static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName MMDevApiData *data = NULL; HRESULT hr; - if(!deviceName) - deviceName = mmDevice; - else if(strcmp(deviceName, mmDevice) != 0) - return ALC_INVALID_VALUE; - //Initialise requested device data = calloc(1, sizeof(MMDevApiData)); if(!data) @@ -630,6 +848,38 @@ static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName if(SUCCEEDED(hr)) { + data->guid = GUID_NULL; + + if(!deviceName) + deviceName = mmDevice; + else if(strcmp(deviceName, mmDevice) != 0) + { + ALuint i; + + if(!PlaybackDeviceList) + { + ThreadRequest req = { data->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE)) + (void)WaitForResponse(&req); + } + + hr = E_FAIL; + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i].name && + strcmp(deviceName, PlaybackDeviceList[i].name) == 0) + { + data->guid = PlaybackDeviceList[i].guid; + hr = S_OK; + break; + } + } + } + } + + if(SUCCEEDED(hr)) + { ThreadRequest req = { data->MsgEvent, 0 }; hr = E_FAIL; @@ -732,14 +982,34 @@ void alcMMDevApiDeinit(void) void alcMMDevApiProbe(enum DevProbe type) { + ThreadRequest req; + HRESULT hr = E_FAIL; + switch(type) { case DEVICE_PROBE: AppendDeviceList(mmDevice); break; + case ALL_DEVICE_PROBE: - AppendAllDeviceList(mmDevice); + req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + if(req.FinishedEvt == NULL) + ERR("Failed to create event: %lu\n", GetLastError()); + else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type)) + hr = WaitForResponse(&req); + if(SUCCEEDED(hr)) + { + ALuint i; + for(i = 0;i < NumPlaybackDevices;i++) + { + if(PlaybackDeviceList[i].name) + AppendAllDeviceList(PlaybackDeviceList[i].name); + } + } + if(req.FinishedEvt != NULL) + CloseHandle(req.FinishedEvt); break; + case CAPTURE_DEVICE_PROBE: break; } diff --git a/Alc/helpers.c b/Alc/helpers.c index c5e2be6c..7a55b8d9 100644 --- a/Alc/helpers.c +++ b/Alc/helpers.c @@ -44,6 +44,14 @@ DEFINE_GUID(IID_IMMDeviceEnumerator, 0xa95664d2, 0x9614, 0x4f35, 0xa7,0x46, 0xd DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2); DEFINE_GUID(IID_IAudioRenderClient, 0xf294acfc, 0x3146, 0x4483, 0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2); +#ifdef HAVE_MMDEVAPI +#include <propkeydef.h> +#include <devpropdef.h> + +DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 4); +DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14); +#endif + #endif #include "alMain.h" |