Lock the device before calling aluHandleDisconnect
[openal-soft.git] / Alc / backends / mmdevapi.c
blob9c934095d7b23cefb21d801d06f81c4d48626deb
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #define COBJMACROS
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <mmdeviceapi.h>
29 #include <audioclient.h>
30 #include <cguid.h>
31 #include <devpropdef.h>
32 #include <mmreg.h>
33 #include <propsys.h>
34 #include <propkey.h>
35 #include <devpkey.h>
36 #ifndef _WAVEFORMATEXTENSIBLE_
37 #include <ks.h>
38 #include <ksmedia.h>
39 #endif
41 #include "alMain.h"
42 #include "alu.h"
45 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
46 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
48 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
50 #define MONO SPEAKER_FRONT_CENTER
51 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
52 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
53 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
54 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
55 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
56 #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)
59 typedef struct {
60 WCHAR *devid;
62 IMMDevice *mmdev;
63 IAudioClient *client;
64 IAudioRenderClient *render;
65 HANDLE NotifyEvent;
67 HANDLE MsgEvent;
69 volatile UINT32 Padding;
71 volatile int killNow;
72 ALvoid *thread;
73 } MMDevApiData;
76 typedef struct {
77 ALCchar *name;
78 WCHAR *devid;
79 } DevMap;
81 static DevMap *PlaybackDeviceList;
82 static ALuint NumPlaybackDevices;
83 static DevMap *CaptureDeviceList;
84 static ALuint NumCaptureDevices;
87 static HANDLE ThreadHdl;
88 static DWORD ThreadID;
90 typedef struct {
91 HANDLE FinishedEvt;
92 HRESULT result;
93 } ThreadRequest;
95 #define WM_USER_OpenDevice (WM_USER+0)
96 #define WM_USER_ResetDevice (WM_USER+1)
97 #define WM_USER_StartDevice (WM_USER+2)
98 #define WM_USER_StopDevice (WM_USER+3)
99 #define WM_USER_CloseDevice (WM_USER+4)
100 #define WM_USER_Enumerate (WM_USER+5)
102 static HRESULT WaitForResponse(ThreadRequest *req)
104 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
105 return req->result;
106 ERR("Message response error: %lu\n", GetLastError());
107 return E_FAIL;
111 static ALCchar *get_device_name(IMMDevice *device)
113 ALCchar *name = NULL;
114 IPropertyStore *ps;
115 PROPVARIANT pvname;
116 HRESULT hr;
117 int len;
119 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
120 if(FAILED(hr))
122 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
123 return calloc(1, 1);
126 PropVariantInit(&pvname);
128 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
129 if(FAILED(hr))
131 WARN("GetValue failed: 0x%08lx\n", hr);
132 name = calloc(1, 1);
134 else
136 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
138 name = calloc(1, len);
139 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL);
143 PropVariantClear(&pvname);
144 IPropertyStore_Release(ps);
146 return name;
149 static void add_device(IMMDevice *device, DevMap *devmap)
151 LPWSTR devid;
152 HRESULT hr;
154 hr = IMMDevice_GetId(device, &devid);
155 if(SUCCEEDED(hr))
157 devmap->devid = strdupW(devid);
158 devmap->name = get_device_name(device);
159 TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid);
160 CoTaskMemFree(devid);
164 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
166 IMMDeviceCollection *coll;
167 IMMDevice *defdev = NULL;
168 DevMap *devlist = NULL;
169 HRESULT hr;
170 UINT count;
171 UINT idx;
172 UINT i;
174 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
175 if(FAILED(hr))
177 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
178 return NULL;
181 idx = count = 0;
182 hr = IMMDeviceCollection_GetCount(coll, &count);
183 if(SUCCEEDED(hr) && count > 0)
185 devlist = calloc(count, sizeof(*devlist));
186 if(!devlist)
188 IMMDeviceCollection_Release(coll);
189 return NULL;
192 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
193 eMultimedia, &defdev);
195 if(SUCCEEDED(hr) && defdev != NULL)
196 add_device(defdev, &devlist[idx++]);
198 for(i = 0;i < count && idx < count;++i)
200 IMMDevice *device;
202 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
203 continue;
205 if(device != defdev)
206 add_device(device, &devlist[idx++]);
208 IMMDevice_Release(device);
211 if(defdev) IMMDevice_Release(defdev);
212 IMMDeviceCollection_Release(coll);
214 *numdevs = idx;
215 return devlist;
219 static ALuint MMDevApiProc(ALvoid *ptr)
221 ALCdevice *device = ptr;
222 MMDevApiData *data = device->ExtraData;
223 UINT32 buffer_len, written;
224 ALuint update_size, len;
225 BYTE *buffer;
226 HRESULT hr;
228 hr = CoInitialize(NULL);
229 if(FAILED(hr))
231 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
232 ALCdevice_Lock(device);
233 aluHandleDisconnect(device);
234 ALCdevice_Unlock(device);
235 return 0;
238 SetRTPriority();
240 update_size = device->UpdateSize;
241 buffer_len = update_size * device->NumUpdates;
242 while(!data->killNow)
244 hr = IAudioClient_GetCurrentPadding(data->client, &written);
245 if(FAILED(hr))
247 ERR("Failed to get padding: 0x%08lx\n", hr);
248 ALCdevice_Lock(device);
249 aluHandleDisconnect(device);
250 ALCdevice_Unlock(device);
251 break;
253 data->Padding = written;
255 len = buffer_len - written;
256 if(len < update_size)
258 DWORD res;
259 res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
260 if(res != WAIT_OBJECT_0)
261 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
262 continue;
264 len -= len%update_size;
266 hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer);
267 if(SUCCEEDED(hr))
269 ALCdevice_Lock(device);
270 aluMixData(device, buffer, len);
271 data->Padding = written + len;
272 ALCdevice_Unlock(device);
273 hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
275 if(FAILED(hr))
277 ERR("Failed to buffer data: 0x%08lx\n", hr);
278 ALCdevice_Lock(device);
279 aluHandleDisconnect(device);
280 ALCdevice_Unlock(device);
281 break;
284 data->Padding = 0;
286 CoUninitialize();
287 return 0;
291 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
293 memset(out, 0, sizeof(*out));
294 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
295 *out = *(const WAVEFORMATEXTENSIBLE*)in;
296 else if(in->wFormatTag == WAVE_FORMAT_PCM)
298 out->Format = *in;
299 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
300 out->Format.cbSize = sizeof(*out) - sizeof(*in);
301 if(out->Format.nChannels == 1)
302 out->dwChannelMask = MONO;
303 else if(out->Format.nChannels == 2)
304 out->dwChannelMask = STEREO;
305 else
306 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
307 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
309 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
311 out->Format = *in;
312 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
313 out->Format.cbSize = sizeof(*out) - sizeof(*in);
314 if(out->Format.nChannels == 1)
315 out->dwChannelMask = MONO;
316 else if(out->Format.nChannels == 2)
317 out->dwChannelMask = STEREO;
318 else
319 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
320 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
322 else
324 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
325 return ALC_FALSE;
327 return ALC_TRUE;
330 static HRESULT DoReset(ALCdevice *device)
332 MMDevApiData *data = device->ExtraData;
333 WAVEFORMATEXTENSIBLE OutputType;
334 WAVEFORMATEX *wfx = NULL;
335 REFERENCE_TIME min_per, buf_time;
336 UINT32 buffer_len, min_len;
337 HRESULT hr;
339 hr = IAudioClient_GetMixFormat(data->client, &wfx);
340 if(FAILED(hr))
342 ERR("Failed to get mix format: 0x%08lx\n", hr);
343 return hr;
346 if(!MakeExtensible(&OutputType, wfx))
348 CoTaskMemFree(wfx);
349 return E_FAIL;
351 CoTaskMemFree(wfx);
352 wfx = NULL;
354 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
355 device->Frequency-1) / device->Frequency;
357 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
358 device->Frequency = OutputType.Format.nSamplesPerSec;
359 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
361 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
362 device->FmtChans = DevFmtMono;
363 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
364 device->FmtChans = DevFmtStereo;
365 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
366 device->FmtChans = DevFmtQuad;
367 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
368 device->FmtChans = DevFmtX51;
369 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
370 device->FmtChans = DevFmtX51Side;
371 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
372 device->FmtChans = DevFmtX61;
373 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
374 device->FmtChans = DevFmtX71;
375 else
376 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
379 switch(device->FmtChans)
381 case DevFmtMono:
382 OutputType.Format.nChannels = 1;
383 OutputType.dwChannelMask = MONO;
384 break;
385 case DevFmtStereo:
386 OutputType.Format.nChannels = 2;
387 OutputType.dwChannelMask = STEREO;
388 break;
389 case DevFmtQuad:
390 OutputType.Format.nChannels = 4;
391 OutputType.dwChannelMask = QUAD;
392 break;
393 case DevFmtX51:
394 OutputType.Format.nChannels = 6;
395 OutputType.dwChannelMask = X5DOT1;
396 break;
397 case DevFmtX51Side:
398 OutputType.Format.nChannels = 6;
399 OutputType.dwChannelMask = X5DOT1SIDE;
400 break;
401 case DevFmtX61:
402 OutputType.Format.nChannels = 7;
403 OutputType.dwChannelMask = X6DOT1;
404 break;
405 case DevFmtX71:
406 OutputType.Format.nChannels = 8;
407 OutputType.dwChannelMask = X7DOT1;
408 break;
410 switch(device->FmtType)
412 case DevFmtByte:
413 device->FmtType = DevFmtUByte;
414 /* fall-through */
415 case DevFmtUByte:
416 OutputType.Format.wBitsPerSample = 8;
417 OutputType.Samples.wValidBitsPerSample = 8;
418 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
419 break;
420 case DevFmtUShort:
421 device->FmtType = DevFmtShort;
422 /* fall-through */
423 case DevFmtShort:
424 OutputType.Format.wBitsPerSample = 16;
425 OutputType.Samples.wValidBitsPerSample = 16;
426 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
427 break;
428 case DevFmtUInt:
429 device->FmtType = DevFmtInt;
430 /* fall-through */
431 case DevFmtInt:
432 OutputType.Format.wBitsPerSample = 32;
433 OutputType.Samples.wValidBitsPerSample = 32;
434 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
435 break;
436 case DevFmtFloat:
437 OutputType.Format.wBitsPerSample = 32;
438 OutputType.Samples.wValidBitsPerSample = 32;
439 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
440 break;
442 OutputType.Format.nSamplesPerSec = device->Frequency;
444 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
445 OutputType.Format.wBitsPerSample / 8;
446 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
447 OutputType.Format.nBlockAlign;
449 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
450 if(FAILED(hr))
452 ERR("Failed to check format support: 0x%08lx\n", hr);
453 hr = IAudioClient_GetMixFormat(data->client, &wfx);
455 if(FAILED(hr))
457 ERR("Failed to find a supported format: 0x%08lx\n", hr);
458 return hr;
461 if(wfx != NULL)
463 if(!MakeExtensible(&OutputType, wfx))
465 CoTaskMemFree(wfx);
466 return E_FAIL;
468 CoTaskMemFree(wfx);
469 wfx = NULL;
471 device->Frequency = OutputType.Format.nSamplesPerSec;
472 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
473 device->FmtChans = DevFmtMono;
474 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
475 device->FmtChans = DevFmtStereo;
476 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
477 device->FmtChans = DevFmtQuad;
478 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
479 device->FmtChans = DevFmtX51;
480 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
481 device->FmtChans = DevFmtX51Side;
482 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
483 device->FmtChans = DevFmtX61;
484 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
485 device->FmtChans = DevFmtX71;
486 else
488 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
489 device->FmtChans = DevFmtStereo;
490 OutputType.Format.nChannels = 2;
491 OutputType.dwChannelMask = STEREO;
494 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
496 if(OutputType.Format.wBitsPerSample == 8)
497 device->FmtType = DevFmtUByte;
498 else if(OutputType.Format.wBitsPerSample == 16)
499 device->FmtType = DevFmtShort;
500 else if(OutputType.Format.wBitsPerSample == 32)
501 device->FmtType = DevFmtInt;
502 else
504 device->FmtType = DevFmtShort;
505 OutputType.Format.wBitsPerSample = 16;
508 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
510 device->FmtType = DevFmtFloat;
511 OutputType.Format.wBitsPerSample = 32;
513 else
515 ERR("Unhandled format sub-type\n");
516 device->FmtType = DevFmtShort;
517 OutputType.Format.wBitsPerSample = 16;
518 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
520 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
523 SetDefaultWFXChannelOrder(device);
525 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
526 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
527 buf_time, 0, &OutputType.Format, NULL);
528 if(FAILED(hr))
530 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
531 return hr;
534 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
535 if(SUCCEEDED(hr))
537 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
538 /* Find the nearest multiple of the period size to the update size */
539 if(min_len < device->UpdateSize)
540 min_len *= (device->UpdateSize + min_len/2)/min_len;
541 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
543 if(FAILED(hr))
545 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
546 return hr;
549 device->UpdateSize = min_len;
550 device->NumUpdates = buffer_len / device->UpdateSize;
551 if(device->NumUpdates <= 1)
553 ERR("Audio client returned buffer_len < period*2; expect break up\n");
554 device->NumUpdates = 2;
555 device->UpdateSize = buffer_len / device->NumUpdates;
558 return hr;
562 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
564 ThreadRequest *req = ptr;
565 IMMDeviceEnumerator *Enumerator;
566 ALuint deviceCount = 0;
567 MMDevApiData *data;
568 ALCdevice *device;
569 HRESULT hr, cohr;
570 MSG msg;
572 TRACE("Starting message thread\n");
574 cohr = CoInitialize(NULL);
575 if(FAILED(cohr))
577 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
578 req->result = cohr;
579 SetEvent(req->FinishedEvt);
580 return 0;
583 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
584 if(FAILED(hr))
586 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
587 CoUninitialize();
588 req->result = hr;
589 SetEvent(req->FinishedEvt);
590 return 0;
592 Enumerator = ptr;
593 IMMDeviceEnumerator_Release(Enumerator);
594 Enumerator = NULL;
596 CoUninitialize();
598 req->result = S_OK;
599 SetEvent(req->FinishedEvt);
601 TRACE("Starting message loop\n");
602 while(GetMessage(&msg, NULL, 0, 0))
604 TRACE("Got message %u\n", msg.message);
605 switch(msg.message)
607 case WM_USER_OpenDevice:
608 req = (ThreadRequest*)msg.wParam;
609 device = (ALCdevice*)msg.lParam;
610 data = device->ExtraData;
612 hr = cohr = S_OK;
613 if(++deviceCount == 1)
614 hr = cohr = CoInitialize(NULL);
615 if(SUCCEEDED(hr))
616 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
617 if(SUCCEEDED(hr))
619 Enumerator = ptr;
620 if(!data->devid)
621 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
622 else
623 hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
624 IMMDeviceEnumerator_Release(Enumerator);
625 Enumerator = NULL;
627 if(SUCCEEDED(hr))
628 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
629 if(SUCCEEDED(hr))
631 data->client = ptr;
632 device->DeviceName = get_device_name(data->mmdev);
635 if(FAILED(hr))
637 if(data->mmdev)
638 IMMDevice_Release(data->mmdev);
639 data->mmdev = NULL;
640 if(--deviceCount == 0 && SUCCEEDED(cohr))
641 CoUninitialize();
644 req->result = hr;
645 SetEvent(req->FinishedEvt);
646 continue;
648 case WM_USER_ResetDevice:
649 req = (ThreadRequest*)msg.wParam;
650 device = (ALCdevice*)msg.lParam;
652 req->result = DoReset(device);
653 SetEvent(req->FinishedEvt);
654 continue;
656 case WM_USER_StartDevice:
657 req = (ThreadRequest*)msg.wParam;
658 device = (ALCdevice*)msg.lParam;
659 data = device->ExtraData;
661 ResetEvent(data->NotifyEvent);
662 hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent);
663 if(FAILED(hr))
664 ERR("Failed to set event handle: 0x%08lx\n", hr);
665 else
667 hr = IAudioClient_Start(data->client);
668 if(FAILED(hr))
669 ERR("Failed to start audio client: 0x%08lx\n", hr);
672 if(SUCCEEDED(hr))
673 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
674 if(SUCCEEDED(hr))
676 data->render = ptr;
677 data->thread = StartThread(MMDevApiProc, device);
678 if(!data->thread)
680 if(data->render)
681 IAudioRenderClient_Release(data->render);
682 data->render = NULL;
683 IAudioClient_Stop(data->client);
684 ERR("Failed to start thread\n");
685 hr = E_FAIL;
689 req->result = hr;
690 SetEvent(req->FinishedEvt);
691 continue;
693 case WM_USER_StopDevice:
694 req = (ThreadRequest*)msg.wParam;
695 device = (ALCdevice*)msg.lParam;
696 data = device->ExtraData;
698 if(data->thread)
700 data->killNow = 1;
701 StopThread(data->thread);
702 data->thread = NULL;
704 data->killNow = 0;
706 IAudioRenderClient_Release(data->render);
707 data->render = NULL;
708 IAudioClient_Stop(data->client);
711 req->result = S_OK;
712 SetEvent(req->FinishedEvt);
713 continue;
715 case WM_USER_CloseDevice:
716 req = (ThreadRequest*)msg.wParam;
717 device = (ALCdevice*)msg.lParam;
718 data = device->ExtraData;
720 IAudioClient_Release(data->client);
721 data->client = NULL;
723 IMMDevice_Release(data->mmdev);
724 data->mmdev = NULL;
726 if(--deviceCount == 0)
727 CoUninitialize();
729 req->result = S_OK;
730 SetEvent(req->FinishedEvt);
731 continue;
733 case WM_USER_Enumerate:
734 req = (ThreadRequest*)msg.wParam;
736 hr = cohr = S_OK;
737 if(++deviceCount == 1)
738 hr = cohr = CoInitialize(NULL);
739 if(SUCCEEDED(hr))
740 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
741 if(SUCCEEDED(hr))
743 EDataFlow flowdir;
744 DevMap **devlist;
745 ALuint *numdevs;
746 ALuint i;
748 Enumerator = ptr;
749 if(msg.lParam == CAPTURE_DEVICE_PROBE)
751 flowdir = eCapture;
752 devlist = &CaptureDeviceList;
753 numdevs = &NumCaptureDevices;
755 else
757 flowdir = eRender;
758 devlist = &PlaybackDeviceList;
759 numdevs = &NumPlaybackDevices;
762 for(i = 0;i < *numdevs;i++)
764 free((*devlist)[i].name);
765 free((*devlist)[i].devid);
767 free(*devlist);
768 *devlist = NULL;
769 *numdevs = 0;
771 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
773 IMMDeviceEnumerator_Release(Enumerator);
774 Enumerator = NULL;
777 if(--deviceCount == 0 && SUCCEEDED(cohr))
778 CoUninitialize();
780 req->result = S_OK;
781 SetEvent(req->FinishedEvt);
782 continue;
784 default:
785 ERR("Unexpected message: %u\n", msg.message);
786 continue;
789 TRACE("Message loop finished\n");
791 return 0;
795 static BOOL MMDevApiLoad(void)
797 static HRESULT InitResult;
798 if(!ThreadHdl)
800 ThreadRequest req;
801 InitResult = E_FAIL;
803 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
804 if(req.FinishedEvt == NULL)
805 ERR("Failed to create event: %lu\n", GetLastError());
806 else
808 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
809 if(ThreadHdl != NULL)
810 InitResult = WaitForResponse(&req);
811 CloseHandle(req.FinishedEvt);
814 return SUCCEEDED(InitResult);
818 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
820 MMDevApiData *data = NULL;
821 HRESULT hr;
823 //Initialise requested device
824 data = calloc(1, sizeof(MMDevApiData));
825 if(!data)
826 return ALC_OUT_OF_MEMORY;
827 device->ExtraData = data;
829 hr = S_OK;
830 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
831 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
832 if(data->NotifyEvent == NULL || data->MsgEvent == NULL)
833 hr = E_FAIL;
835 if(SUCCEEDED(hr))
837 if(deviceName)
839 ALuint i;
841 if(!PlaybackDeviceList)
843 ThreadRequest req = { data->MsgEvent, 0 };
844 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
845 (void)WaitForResponse(&req);
848 hr = E_FAIL;
849 for(i = 0;i < NumPlaybackDevices;i++)
851 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
853 data->devid = strdupW(PlaybackDeviceList[i].devid);
854 hr = S_OK;
855 break;
861 if(SUCCEEDED(hr))
863 ThreadRequest req = { data->MsgEvent, 0 };
865 hr = E_FAIL;
866 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
867 hr = WaitForResponse(&req);
870 if(FAILED(hr))
872 if(data->NotifyEvent != NULL)
873 CloseHandle(data->NotifyEvent);
874 data->NotifyEvent = NULL;
875 if(data->MsgEvent != NULL)
876 CloseHandle(data->MsgEvent);
877 data->MsgEvent = NULL;
879 free(data);
880 device->ExtraData = NULL;
882 ERR("Device init failed: 0x%08lx\n", hr);
883 return ALC_INVALID_VALUE;
886 return ALC_NO_ERROR;
889 static void MMDevApiClosePlayback(ALCdevice *device)
891 MMDevApiData *data = device->ExtraData;
892 ThreadRequest req = { data->MsgEvent, 0 };
894 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
895 (void)WaitForResponse(&req);
897 CloseHandle(data->MsgEvent);
898 data->MsgEvent = NULL;
900 CloseHandle(data->NotifyEvent);
901 data->NotifyEvent = NULL;
903 free(data->devid);
904 data->devid = NULL;
906 free(data);
907 device->ExtraData = NULL;
910 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
912 MMDevApiData *data = device->ExtraData;
913 ThreadRequest req = { data->MsgEvent, 0 };
914 HRESULT hr = E_FAIL;
916 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
917 hr = WaitForResponse(&req);
919 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
922 static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
924 MMDevApiData *data = device->ExtraData;
925 ThreadRequest req = { data->MsgEvent, 0 };
926 HRESULT hr = E_FAIL;
928 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
929 hr = WaitForResponse(&req);
931 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
934 static void MMDevApiStopPlayback(ALCdevice *device)
936 MMDevApiData *data = device->ExtraData;
937 ThreadRequest req = { data->MsgEvent, 0 };
939 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
940 (void)WaitForResponse(&req);
944 static ALint64 MMDevApiGetLatency(ALCdevice *device)
946 MMDevApiData *data = device->ExtraData;
948 return (ALint64)data->Padding * 1000000000 / device->Frequency;
952 static const BackendFuncs MMDevApiFuncs = {
953 MMDevApiOpenPlayback,
954 MMDevApiClosePlayback,
955 MMDevApiResetPlayback,
956 MMDevApiStartPlayback,
957 MMDevApiStopPlayback,
958 NULL,
959 NULL,
960 NULL,
961 NULL,
962 NULL,
963 NULL,
964 ALCdevice_LockDefault,
965 ALCdevice_UnlockDefault,
966 MMDevApiGetLatency
970 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
972 if(!MMDevApiLoad())
973 return ALC_FALSE;
974 *FuncList = MMDevApiFuncs;
975 return ALC_TRUE;
978 void alcMMDevApiDeinit(void)
980 ALuint i;
982 for(i = 0;i < NumPlaybackDevices;i++)
984 free(PlaybackDeviceList[i].name);
985 free(PlaybackDeviceList[i].devid);
987 free(PlaybackDeviceList);
988 PlaybackDeviceList = NULL;
989 NumPlaybackDevices = 0;
991 for(i = 0;i < NumCaptureDevices;i++)
993 free(CaptureDeviceList[i].name);
994 free(CaptureDeviceList[i].devid);
996 free(CaptureDeviceList);
997 CaptureDeviceList = NULL;
998 NumCaptureDevices = 0;
1000 if(ThreadHdl)
1002 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
1003 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
1004 CloseHandle(ThreadHdl);
1005 ThreadHdl = NULL;
1009 void alcMMDevApiProbe(enum DevProbe type)
1011 ThreadRequest req = { NULL, 0 };
1012 HRESULT hr = E_FAIL;
1014 switch(type)
1016 case ALL_DEVICE_PROBE:
1017 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
1018 if(req.FinishedEvt == NULL)
1019 ERR("Failed to create event: %lu\n", GetLastError());
1020 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
1021 hr = WaitForResponse(&req);
1022 if(SUCCEEDED(hr))
1024 ALuint i;
1025 for(i = 0;i < NumPlaybackDevices;i++)
1027 if(PlaybackDeviceList[i].name)
1028 AppendAllDevicesList(PlaybackDeviceList[i].name);
1031 break;
1033 case CAPTURE_DEVICE_PROBE:
1034 break;
1036 if(req.FinishedEvt != NULL)
1037 CloseHandle(req.FinishedEvt);
1038 req.FinishedEvt = NULL;