diff options
author | Chris Robinson <[email protected]> | 2011-08-03 10:42:29 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2011-08-03 10:42:29 -0700 |
commit | f7c176e8ca64c33535db54a284aa1aef4c31a6a8 (patch) | |
tree | 1e991558a70dd465a8ccc0b61b654bdbe2191378 /Alc | |
parent | 3e2bc847d815e8762da753cf60e7e49b61492699 (diff) |
Use a message thread to handle MMDevAPI backend methods
Because MMDevAPI uses COM, CoInitialize must be called on the thread(s) where
the device calls are made. OpenAL has no such requirement, thus no guarantee
that COM will be (or remain) initialized on the calling thread(s).
Diffstat (limited to 'Alc')
-rw-r--r-- | Alc/mmdevapi.c | 456 |
1 files changed, 309 insertions, 147 deletions
diff --git a/Alc/mmdevapi.c b/Alc/mmdevapi.c index ffae9745..d7d85a20 100644 --- a/Alc/mmdevapi.c +++ b/Alc/mmdevapi.c @@ -52,14 +52,13 @@ DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT) -static IMMDeviceEnumerator *Enumerator = NULL; - - typedef struct { IMMDevice *mmdev; IAudioClient *client; HANDLE hNotifyEvent; + HANDLE MsgEvent; + volatile int killNow; ALvoid *thread; } MMDevApiData; @@ -68,72 +67,25 @@ typedef struct { static const ALCchar mmDevice[] = "WASAPI Default"; -static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) -{ - memset(out, 0, sizeof(*out)); - if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) - *out = *(WAVEFORMATEXTENSIBLE*)in; - else if(in->wFormatTag == WAVE_FORMAT_PCM) - { - out->Format = *in; - out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - out->Format.cbSize = sizeof(*out) - sizeof(*in); - if(out->Format.nChannels == 1) - out->dwChannelMask = MONO; - else if(out->Format.nChannels == 2) - out->dwChannelMask = STEREO; - else - ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels); - out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - } - else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) - { - out->Format = *in; - out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - out->Format.cbSize = sizeof(*out) - sizeof(*in); - if(out->Format.nChannels == 1) - out->dwChannelMask = MONO; - else if(out->Format.nChannels == 2) - out->dwChannelMask = STEREO; - else - ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels); - out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - } - else - { - ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag); - return ALC_FALSE; - } - return ALC_TRUE; -} +static HANDLE ThreadHdl; +static DWORD ThreadID; +typedef struct { + HANDLE FinishedEvt; + HRESULT result; +} ThreadRequest; + +#define WM_USER_OpenDevice (WM_USER+0) +#define WM_USER_ResetDevice (WM_USER+1) +#define WM_USER_StopDevice (WM_USER+2) +#define WM_USER_CloseDevice (WM_USER+3) -static void *MMDevApiLoad(void) +static HRESULT WaitForResponse(ThreadRequest *req) { - if(!Enumerator) - { - void *mme = NULL; - HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); - if(FAILED(hr)) - { - WARN("Failed to initialize apartment-threaded COM: 0x%08lx\n", hr); - hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); - if(FAILED(hr)) - WARN("Failed to initialize multi-threaded COM: 0x%08lx\n", hr); - } - if(SUCCEEDED(hr)) - { - hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &mme); - if(SUCCEEDED(hr)) - Enumerator = mme; - else - { - WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); - CoUninitialize(); - } - } - } - return Enumerator; + if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0) + return req->result; + ERR("Message response error: %lu\n", GetLastError()); + return E_FAIL; } @@ -149,6 +101,14 @@ static ALuint MMDevApiProc(ALvoid *ptr) BYTE *buffer; HRESULT hr; + hr = CoInitialize(NULL); + if(FAILED(hr)) + { + ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr); + aluHandleDisconnect(device); + return 0; + } + hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr); if(FAILED(hr)) { @@ -195,81 +155,52 @@ static ALuint MMDevApiProc(ALvoid *ptr) } IAudioRenderClient_Release(render.iface); + + CoUninitialize(); return 0; } -static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in) { - MMDevApiData *data = NULL; - void *client = NULL; - HRESULT hr; - - if(!MMDevApiLoad()) - return ALC_FALSE; - - if(!deviceName) - deviceName = mmDevice; - else if(strcmp(deviceName, mmDevice) != 0) - return ALC_FALSE; - - //Initialise requested device - data = calloc(1, sizeof(MMDevApiData)); - if(!data) + memset(out, 0, sizeof(*out)); + if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + *out = *(WAVEFORMATEXTENSIBLE*)in; + else if(in->wFormatTag == WAVE_FORMAT_PCM) { - alcSetError(device, ALC_OUT_OF_MEMORY); - return ALC_FALSE; + out->Format = *in; + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + out->Format.cbSize = sizeof(*out) - sizeof(*in); + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; } - - hr = S_OK; - data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); - if(data->hNotifyEvent == NULL) - hr = E_FAIL; - - //MMDevApi Init code - if(SUCCEEDED(hr)) - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); - if(SUCCEEDED(hr)) - hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &client); - - if(FAILED(hr)) + else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { - if(data->mmdev) - IMMDevice_Release(data->mmdev); - data->mmdev = NULL; - if(data->hNotifyEvent != NULL) - CloseHandle(data->hNotifyEvent); - data->hNotifyEvent = NULL; - free(data); - - ERR("Device init failed: 0x%08lx\n", hr); + out->Format = *in; + out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + out->Format.cbSize = sizeof(*out) - sizeof(*in); + if(out->Format.nChannels == 1) + out->dwChannelMask = MONO; + else if(out->Format.nChannels == 2) + out->dwChannelMask = STEREO; + else + ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels); + out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + } + else + { + ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag); return ALC_FALSE; } - data->client = client; - - device->szDeviceName = strdup(deviceName); - device->ExtraData = data; return ALC_TRUE; } -static void MMDevApiClosePlayback(ALCdevice *device) -{ - MMDevApiData *data = device->ExtraData; - - IAudioClient_Release(data->client); - data->client = NULL; - - IMMDevice_Release(data->mmdev); - data->mmdev = NULL; - - CloseHandle(data->hNotifyEvent); - data->hNotifyEvent = NULL; - - free(data); - device->ExtraData = NULL; -} - -static ALCboolean MMDevApiResetPlayback(ALCdevice *device) +static HRESULT DoReset(ALCdevice *device) { MMDevApiData *data = device->ExtraData; WAVEFORMATEXTENSIBLE OutputType; @@ -282,13 +213,13 @@ static ALCboolean MMDevApiResetPlayback(ALCdevice *device) if(FAILED(hr)) { ERR("Failed to get mix format: 0x%08lx\n", hr); - return ALC_FALSE; + return hr; } if(!MakeExtensible(&OutputType, wfx)) { CoTaskMemFree(wfx); - return ALC_FALSE; + return E_FAIL; } CoTaskMemFree(wfx); wfx = NULL; @@ -386,7 +317,7 @@ static ALCboolean MMDevApiResetPlayback(ALCdevice *device) if(FAILED(hr)) { ERR("Failed to find a supported format: 0x%08lx\n", hr); - return ALC_FALSE; + return hr; } if(wfx != NULL) @@ -394,7 +325,7 @@ static ALCboolean MMDevApiResetPlayback(ALCdevice *device) if(!MakeExtensible(&OutputType, wfx)) { CoTaskMemFree(wfx); - return ALC_FALSE; + return E_FAIL; } CoTaskMemFree(wfx); wfx = NULL; @@ -513,14 +444,14 @@ static ALCboolean MMDevApiResetPlayback(ALCdevice *device) if(FAILED(hr)) { ERR("Failed to initialize audio client: 0x%08lx\n", hr); - return ALC_FALSE; + return hr; } hr = IAudioClient_GetBufferSize(data->client, &buffer_len); if(FAILED(hr)) { ERR("Failed to get audio buffer info: 0x%08lx\n", hr); - return ALC_FALSE; + return hr; } device->NumUpdates = buffer_len / device->UpdateSize; @@ -537,7 +468,7 @@ static ALCboolean MMDevApiResetPlayback(ALCdevice *device) if(FAILED(hr)) { ERR("Failed to start audio client: 0x%08lx\n", hr); - return ALC_FALSE; + return hr; } data->thread = StartThread(MMDevApiProc, device); @@ -545,26 +476,256 @@ static ALCboolean MMDevApiResetPlayback(ALCdevice *device) { IAudioClient_Stop(data->client); ERR("Failed to start thread\n"); + return E_FAIL; + } + + return hr; +} + + +static DWORD CALLBACK MessageProc(void *ptr) +{ + ThreadRequest *req = ptr; + IMMDeviceEnumerator *Enumerator; + MMDevApiData *data; + ALCdevice *device; + HRESULT hr; + MSG msg; + + TRACE("Starting message thread\n"); + + hr = CoInitialize(NULL); + if(FAILED(hr)) + { + WARN("Failed to initialize COM: 0x%08lx\n", hr); + req->result = hr; + SetEvent(req->FinishedEvt); + return 0; + } + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(FAILED(hr)) + { + WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr); + CoUninitialize(); + req->result = hr; + SetEvent(req->FinishedEvt); + return 0; + } + Enumerator = ptr; + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + + req->result = S_OK; + SetEvent(req->FinishedEvt); + + TRACE("Starting message loop\n"); + while(GetMessage(&msg, NULL, 0, 0)) + { + TRACE("Got message %u\n", msg.message); + switch(msg.message) + { + case WM_USER_OpenDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr); + if(SUCCEEDED(hr)) + { + Enumerator = ptr; + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev); + IMMDeviceEnumerator_Release(Enumerator); + Enumerator = NULL; + } + if(SUCCEEDED(hr)) + hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr); + if(SUCCEEDED(hr)) + data->client = ptr; + + if(FAILED(hr)) + { + if(data->mmdev) + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; + } + + req->result = hr; + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_ResetDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + + req->result = DoReset(device); + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_StopDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + if(data->thread) + { + data->killNow = 1; + StopThread(data->thread); + data->thread = NULL; + + data->killNow = 0; + + IAudioClient_Stop(data->client); + } + + req->result = S_OK; + SetEvent(req->FinishedEvt); + continue; + + case WM_USER_CloseDevice: + req = (ThreadRequest*)msg.wParam; + device = (ALCdevice*)msg.lParam; + data = device->ExtraData; + + IAudioClient_Release(data->client); + data->client = NULL; + + IMMDevice_Release(data->mmdev); + data->mmdev = NULL; + + req->result = S_OK; + SetEvent(req->FinishedEvt); + continue; + + default: + ERR("Unexpected message: %u\n", msg.message); + continue; + } + } + TRACE("Message loop finished\n"); + + CoUninitialize(); + return 0; +} + + +static BOOL MMDevApiLoad(void) +{ + static HRESULT InitResult; + if(!ThreadHdl) + { + ThreadRequest req; + InitResult = E_FAIL; + + req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL); + if(req.FinishedEvt == NULL) + ERR("Failed to create event: %lu\n", GetLastError()); + else + { + ThreadHdl = CreateThread(NULL, 0, MessageProc, &req, 0, &ThreadID); + if(ThreadHdl != NULL) + InitResult = WaitForResponse(&req); + CloseHandle(req.FinishedEvt); + } + } + return SUCCEEDED(InitResult); +} + + +static ALCboolean MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName) +{ + MMDevApiData *data = NULL; + HRESULT hr; + + if(!MMDevApiLoad()) + return ALC_FALSE; + + if(!deviceName) + deviceName = mmDevice; + else if(strcmp(deviceName, mmDevice) != 0) + return ALC_FALSE; + + //Initialise requested device + data = calloc(1, sizeof(MMDevApiData)); + if(!data) + { + alcSetError(device, ALC_OUT_OF_MEMORY); + return ALC_FALSE; + } + device->ExtraData = data; + + hr = S_OK; + data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + if(data->hNotifyEvent == NULL || data->MsgEvent == NULL) + hr = E_FAIL; + + if(SUCCEEDED(hr)) + { + ThreadRequest req = { data->MsgEvent, 0 }; + + hr = E_FAIL; + if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device)) + hr = WaitForResponse(&req); + } + + if(FAILED(hr)) + { + if(data->hNotifyEvent != NULL) + CloseHandle(data->hNotifyEvent); + data->hNotifyEvent = NULL; + if(data->MsgEvent != NULL) + CloseHandle(data->MsgEvent); + data->MsgEvent = NULL; + + free(data); + device->ExtraData = NULL; + + ERR("Device init failed: 0x%08lx\n", hr); return ALC_FALSE; } + device->szDeviceName = strdup(deviceName); return ALC_TRUE; } -static void MMDevApiStopPlayback(ALCdevice *device) +static void MMDevApiClosePlayback(ALCdevice *device) { MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; - if(!data->thread) - return; + if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device)) + (void)WaitForResponse(&req); - data->killNow = 1; - StopThread(data->thread); - data->thread = NULL; + CloseHandle(data->MsgEvent); + data->MsgEvent = NULL; - data->killNow = 0; + CloseHandle(data->hNotifyEvent); + data->hNotifyEvent = NULL; - IAudioClient_Stop(data->client); + free(data); + device->ExtraData = NULL; +} + +static ALCboolean MMDevApiResetPlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + HRESULT hr = E_FAIL; + + if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device)) + hr = WaitForResponse(&req); + + return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE; +} + +static void MMDevApiStopPlayback(ALCdevice *device) +{ + MMDevApiData *data = device->ExtraData; + ThreadRequest req = { data->MsgEvent, 0 }; + + if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device)) + (void)WaitForResponse(&req); } @@ -597,11 +758,12 @@ void alcMMDevApiInit(BackendFuncs *FuncList) void alcMMDevApiDeinit(void) { - if(Enumerator) + if(ThreadHdl) { - IMMDeviceEnumerator_Release(Enumerator); - Enumerator = NULL; - CoUninitialize(); + TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID); + PostThreadMessage(ThreadID, WM_QUIT, 0, 0); + CloseHandle(ThreadHdl); + ThreadHdl = NULL; } } |