Filter unneeded thread messages with MMDevApi
[openal-soft.git] / Alc / backends / mmdevapi.c
blob2555c7f3693113d5638c1a09b56a1d8046f2cd6b
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_First (WM_USER+0)
96 #define WM_USER_OpenDevice (WM_USER+0)
97 #define WM_USER_ResetDevice (WM_USER+1)
98 #define WM_USER_StartDevice (WM_USER+2)
99 #define WM_USER_StopDevice (WM_USER+3)
100 #define WM_USER_CloseDevice (WM_USER+4)
101 #define WM_USER_Enumerate (WM_USER+5)
102 #define WM_USER_Last (WM_USER+5)
104 static HRESULT WaitForResponse(ThreadRequest *req)
106 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
107 return req->result;
108 ERR("Message response error: %lu\n", GetLastError());
109 return E_FAIL;
113 static ALCchar *get_device_name(IMMDevice *device)
115 ALCchar *name = NULL;
116 IPropertyStore *ps;
117 PROPVARIANT pvname;
118 HRESULT hr;
119 int len;
121 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
122 if(FAILED(hr))
124 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
125 return calloc(1, 1);
128 PropVariantInit(&pvname);
130 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
131 if(FAILED(hr))
133 WARN("GetValue failed: 0x%08lx\n", hr);
134 name = calloc(1, 1);
136 else
138 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
140 name = calloc(1, len);
141 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL);
145 PropVariantClear(&pvname);
146 IPropertyStore_Release(ps);
148 return name;
151 static void add_device(IMMDevice *device, DevMap *devmap)
153 LPWSTR devid;
154 HRESULT hr;
156 hr = IMMDevice_GetId(device, &devid);
157 if(SUCCEEDED(hr))
159 devmap->devid = strdupW(devid);
160 devmap->name = get_device_name(device);
161 TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid);
162 CoTaskMemFree(devid);
166 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
168 IMMDeviceCollection *coll;
169 IMMDevice *defdev = NULL;
170 DevMap *devlist = NULL;
171 HRESULT hr;
172 UINT count;
173 UINT idx;
174 UINT i;
176 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
177 if(FAILED(hr))
179 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
180 return NULL;
183 idx = count = 0;
184 hr = IMMDeviceCollection_GetCount(coll, &count);
185 if(SUCCEEDED(hr) && count > 0)
187 devlist = calloc(count, sizeof(*devlist));
188 if(!devlist)
190 IMMDeviceCollection_Release(coll);
191 return NULL;
194 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
195 eMultimedia, &defdev);
197 if(SUCCEEDED(hr) && defdev != NULL)
198 add_device(defdev, &devlist[idx++]);
200 for(i = 0;i < count && idx < count;++i)
202 IMMDevice *device;
204 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
205 continue;
207 if(device != defdev)
208 add_device(device, &devlist[idx++]);
210 IMMDevice_Release(device);
213 if(defdev) IMMDevice_Release(defdev);
214 IMMDeviceCollection_Release(coll);
216 *numdevs = idx;
217 return devlist;
221 static ALuint MMDevApiProc(ALvoid *ptr)
223 ALCdevice *device = ptr;
224 MMDevApiData *data = device->ExtraData;
225 UINT32 buffer_len, written;
226 ALuint update_size, len;
227 BYTE *buffer;
228 HRESULT hr;
230 hr = CoInitialize(NULL);
231 if(FAILED(hr))
233 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
234 ALCdevice_Lock(device);
235 aluHandleDisconnect(device);
236 ALCdevice_Unlock(device);
237 return 0;
240 SetRTPriority();
242 update_size = device->UpdateSize;
243 buffer_len = update_size * device->NumUpdates;
244 while(!data->killNow)
246 hr = IAudioClient_GetCurrentPadding(data->client, &written);
247 if(FAILED(hr))
249 ERR("Failed to get padding: 0x%08lx\n", hr);
250 ALCdevice_Lock(device);
251 aluHandleDisconnect(device);
252 ALCdevice_Unlock(device);
253 break;
255 data->Padding = written;
257 len = buffer_len - written;
258 if(len < update_size)
260 DWORD res;
261 res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
262 if(res != WAIT_OBJECT_0)
263 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
264 continue;
266 len -= len%update_size;
268 hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer);
269 if(SUCCEEDED(hr))
271 ALCdevice_Lock(device);
272 aluMixData(device, buffer, len);
273 data->Padding = written + len;
274 ALCdevice_Unlock(device);
275 hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
277 if(FAILED(hr))
279 ERR("Failed to buffer data: 0x%08lx\n", hr);
280 ALCdevice_Lock(device);
281 aluHandleDisconnect(device);
282 ALCdevice_Unlock(device);
283 break;
286 data->Padding = 0;
288 CoUninitialize();
289 return 0;
293 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
295 memset(out, 0, sizeof(*out));
296 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
297 *out = *(const WAVEFORMATEXTENSIBLE*)in;
298 else if(in->wFormatTag == WAVE_FORMAT_PCM)
300 out->Format = *in;
301 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
302 out->Format.cbSize = sizeof(*out) - sizeof(*in);
303 if(out->Format.nChannels == 1)
304 out->dwChannelMask = MONO;
305 else if(out->Format.nChannels == 2)
306 out->dwChannelMask = STEREO;
307 else
308 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
309 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
311 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
313 out->Format = *in;
314 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
315 out->Format.cbSize = sizeof(*out) - sizeof(*in);
316 if(out->Format.nChannels == 1)
317 out->dwChannelMask = MONO;
318 else if(out->Format.nChannels == 2)
319 out->dwChannelMask = STEREO;
320 else
321 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
322 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
324 else
326 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
327 return ALC_FALSE;
329 return ALC_TRUE;
332 static HRESULT DoReset(ALCdevice *device)
334 MMDevApiData *data = device->ExtraData;
335 WAVEFORMATEXTENSIBLE OutputType;
336 WAVEFORMATEX *wfx = NULL;
337 REFERENCE_TIME min_per, buf_time;
338 UINT32 buffer_len, min_len;
339 HRESULT hr;
341 hr = IAudioClient_GetMixFormat(data->client, &wfx);
342 if(FAILED(hr))
344 ERR("Failed to get mix format: 0x%08lx\n", hr);
345 return hr;
348 if(!MakeExtensible(&OutputType, wfx))
350 CoTaskMemFree(wfx);
351 return E_FAIL;
353 CoTaskMemFree(wfx);
354 wfx = NULL;
356 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
357 device->Frequency-1) / device->Frequency;
359 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
360 device->Frequency = OutputType.Format.nSamplesPerSec;
361 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
363 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
364 device->FmtChans = DevFmtMono;
365 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
366 device->FmtChans = DevFmtStereo;
367 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
368 device->FmtChans = DevFmtQuad;
369 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
370 device->FmtChans = DevFmtX51;
371 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
372 device->FmtChans = DevFmtX51Side;
373 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
374 device->FmtChans = DevFmtX61;
375 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
376 device->FmtChans = DevFmtX71;
377 else
378 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
381 switch(device->FmtChans)
383 case DevFmtMono:
384 OutputType.Format.nChannels = 1;
385 OutputType.dwChannelMask = MONO;
386 break;
387 case DevFmtStereo:
388 OutputType.Format.nChannels = 2;
389 OutputType.dwChannelMask = STEREO;
390 break;
391 case DevFmtQuad:
392 OutputType.Format.nChannels = 4;
393 OutputType.dwChannelMask = QUAD;
394 break;
395 case DevFmtX51:
396 OutputType.Format.nChannels = 6;
397 OutputType.dwChannelMask = X5DOT1;
398 break;
399 case DevFmtX51Side:
400 OutputType.Format.nChannels = 6;
401 OutputType.dwChannelMask = X5DOT1SIDE;
402 break;
403 case DevFmtX61:
404 OutputType.Format.nChannels = 7;
405 OutputType.dwChannelMask = X6DOT1;
406 break;
407 case DevFmtX71:
408 OutputType.Format.nChannels = 8;
409 OutputType.dwChannelMask = X7DOT1;
410 break;
412 switch(device->FmtType)
414 case DevFmtByte:
415 device->FmtType = DevFmtUByte;
416 /* fall-through */
417 case DevFmtUByte:
418 OutputType.Format.wBitsPerSample = 8;
419 OutputType.Samples.wValidBitsPerSample = 8;
420 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
421 break;
422 case DevFmtUShort:
423 device->FmtType = DevFmtShort;
424 /* fall-through */
425 case DevFmtShort:
426 OutputType.Format.wBitsPerSample = 16;
427 OutputType.Samples.wValidBitsPerSample = 16;
428 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
429 break;
430 case DevFmtUInt:
431 device->FmtType = DevFmtInt;
432 /* fall-through */
433 case DevFmtInt:
434 OutputType.Format.wBitsPerSample = 32;
435 OutputType.Samples.wValidBitsPerSample = 32;
436 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
437 break;
438 case DevFmtFloat:
439 OutputType.Format.wBitsPerSample = 32;
440 OutputType.Samples.wValidBitsPerSample = 32;
441 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
442 break;
444 OutputType.Format.nSamplesPerSec = device->Frequency;
446 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
447 OutputType.Format.wBitsPerSample / 8;
448 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
449 OutputType.Format.nBlockAlign;
451 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
452 if(FAILED(hr))
454 ERR("Failed to check format support: 0x%08lx\n", hr);
455 hr = IAudioClient_GetMixFormat(data->client, &wfx);
457 if(FAILED(hr))
459 ERR("Failed to find a supported format: 0x%08lx\n", hr);
460 return hr;
463 if(wfx != NULL)
465 if(!MakeExtensible(&OutputType, wfx))
467 CoTaskMemFree(wfx);
468 return E_FAIL;
470 CoTaskMemFree(wfx);
471 wfx = NULL;
473 device->Frequency = OutputType.Format.nSamplesPerSec;
474 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
475 device->FmtChans = DevFmtMono;
476 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
477 device->FmtChans = DevFmtStereo;
478 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
479 device->FmtChans = DevFmtQuad;
480 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
481 device->FmtChans = DevFmtX51;
482 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
483 device->FmtChans = DevFmtX51Side;
484 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
485 device->FmtChans = DevFmtX61;
486 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
487 device->FmtChans = DevFmtX71;
488 else
490 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
491 device->FmtChans = DevFmtStereo;
492 OutputType.Format.nChannels = 2;
493 OutputType.dwChannelMask = STEREO;
496 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
498 if(OutputType.Format.wBitsPerSample == 8)
499 device->FmtType = DevFmtUByte;
500 else if(OutputType.Format.wBitsPerSample == 16)
501 device->FmtType = DevFmtShort;
502 else if(OutputType.Format.wBitsPerSample == 32)
503 device->FmtType = DevFmtInt;
504 else
506 device->FmtType = DevFmtShort;
507 OutputType.Format.wBitsPerSample = 16;
510 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
512 device->FmtType = DevFmtFloat;
513 OutputType.Format.wBitsPerSample = 32;
515 else
517 ERR("Unhandled format sub-type\n");
518 device->FmtType = DevFmtShort;
519 OutputType.Format.wBitsPerSample = 16;
520 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
522 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
525 SetDefaultWFXChannelOrder(device);
527 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
528 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
529 buf_time, 0, &OutputType.Format, NULL);
530 if(FAILED(hr))
532 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
533 return hr;
536 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
537 if(SUCCEEDED(hr))
539 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
540 /* Find the nearest multiple of the period size to the update size */
541 if(min_len < device->UpdateSize)
542 min_len *= (device->UpdateSize + min_len/2)/min_len;
543 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
545 if(FAILED(hr))
547 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
548 return hr;
551 device->UpdateSize = min_len;
552 device->NumUpdates = buffer_len / device->UpdateSize;
553 if(device->NumUpdates <= 1)
555 ERR("Audio client returned buffer_len < period*2; expect break up\n");
556 device->NumUpdates = 2;
557 device->UpdateSize = buffer_len / device->NumUpdates;
560 return hr;
564 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
566 ThreadRequest *req = ptr;
567 IMMDeviceEnumerator *Enumerator;
568 ALuint deviceCount = 0;
569 MMDevApiData *data;
570 ALCdevice *device;
571 HRESULT hr, cohr;
572 MSG msg;
574 TRACE("Starting message thread\n");
576 cohr = CoInitialize(NULL);
577 if(FAILED(cohr))
579 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
580 req->result = cohr;
581 SetEvent(req->FinishedEvt);
582 return 0;
585 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
586 if(FAILED(hr))
588 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
589 CoUninitialize();
590 req->result = hr;
591 SetEvent(req->FinishedEvt);
592 return 0;
594 Enumerator = ptr;
595 IMMDeviceEnumerator_Release(Enumerator);
596 Enumerator = NULL;
598 CoUninitialize();
600 req->result = S_OK;
601 SetEvent(req->FinishedEvt);
603 TRACE("Starting message loop\n");
604 while(GetMessage(&msg, NULL, WM_USER_First, WM_USER_Last))
606 TRACE("Got message %u\n", msg.message);
607 switch(msg.message)
609 case WM_USER_OpenDevice:
610 req = (ThreadRequest*)msg.wParam;
611 device = (ALCdevice*)msg.lParam;
612 data = device->ExtraData;
614 hr = cohr = S_OK;
615 if(++deviceCount == 1)
616 hr = cohr = CoInitialize(NULL);
617 if(SUCCEEDED(hr))
618 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
619 if(SUCCEEDED(hr))
621 Enumerator = ptr;
622 if(!data->devid)
623 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
624 else
625 hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
626 IMMDeviceEnumerator_Release(Enumerator);
627 Enumerator = NULL;
629 if(SUCCEEDED(hr))
630 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
631 if(SUCCEEDED(hr))
633 data->client = ptr;
634 device->DeviceName = get_device_name(data->mmdev);
637 if(FAILED(hr))
639 if(data->mmdev)
640 IMMDevice_Release(data->mmdev);
641 data->mmdev = NULL;
642 if(--deviceCount == 0 && SUCCEEDED(cohr))
643 CoUninitialize();
646 req->result = hr;
647 SetEvent(req->FinishedEvt);
648 continue;
650 case WM_USER_ResetDevice:
651 req = (ThreadRequest*)msg.wParam;
652 device = (ALCdevice*)msg.lParam;
654 req->result = DoReset(device);
655 SetEvent(req->FinishedEvt);
656 continue;
658 case WM_USER_StartDevice:
659 req = (ThreadRequest*)msg.wParam;
660 device = (ALCdevice*)msg.lParam;
661 data = device->ExtraData;
663 ResetEvent(data->NotifyEvent);
664 hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent);
665 if(FAILED(hr))
666 ERR("Failed to set event handle: 0x%08lx\n", hr);
667 else
669 hr = IAudioClient_Start(data->client);
670 if(FAILED(hr))
671 ERR("Failed to start audio client: 0x%08lx\n", hr);
674 if(SUCCEEDED(hr))
675 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
676 if(SUCCEEDED(hr))
678 data->render = ptr;
679 data->thread = StartThread(MMDevApiProc, device);
680 if(!data->thread)
682 if(data->render)
683 IAudioRenderClient_Release(data->render);
684 data->render = NULL;
685 IAudioClient_Stop(data->client);
686 ERR("Failed to start thread\n");
687 hr = E_FAIL;
691 req->result = hr;
692 SetEvent(req->FinishedEvt);
693 continue;
695 case WM_USER_StopDevice:
696 req = (ThreadRequest*)msg.wParam;
697 device = (ALCdevice*)msg.lParam;
698 data = device->ExtraData;
700 if(data->thread)
702 data->killNow = 1;
703 StopThread(data->thread);
704 data->thread = NULL;
706 data->killNow = 0;
708 IAudioRenderClient_Release(data->render);
709 data->render = NULL;
710 IAudioClient_Stop(data->client);
713 req->result = S_OK;
714 SetEvent(req->FinishedEvt);
715 continue;
717 case WM_USER_CloseDevice:
718 req = (ThreadRequest*)msg.wParam;
719 device = (ALCdevice*)msg.lParam;
720 data = device->ExtraData;
722 IAudioClient_Release(data->client);
723 data->client = NULL;
725 IMMDevice_Release(data->mmdev);
726 data->mmdev = NULL;
728 if(--deviceCount == 0)
729 CoUninitialize();
731 req->result = S_OK;
732 SetEvent(req->FinishedEvt);
733 continue;
735 case WM_USER_Enumerate:
736 req = (ThreadRequest*)msg.wParam;
738 hr = cohr = S_OK;
739 if(++deviceCount == 1)
740 hr = cohr = CoInitialize(NULL);
741 if(SUCCEEDED(hr))
742 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
743 if(SUCCEEDED(hr))
745 EDataFlow flowdir;
746 DevMap **devlist;
747 ALuint *numdevs;
748 ALuint i;
750 Enumerator = ptr;
751 if(msg.lParam == CAPTURE_DEVICE_PROBE)
753 flowdir = eCapture;
754 devlist = &CaptureDeviceList;
755 numdevs = &NumCaptureDevices;
757 else
759 flowdir = eRender;
760 devlist = &PlaybackDeviceList;
761 numdevs = &NumPlaybackDevices;
764 for(i = 0;i < *numdevs;i++)
766 free((*devlist)[i].name);
767 free((*devlist)[i].devid);
769 free(*devlist);
770 *devlist = NULL;
771 *numdevs = 0;
773 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
775 IMMDeviceEnumerator_Release(Enumerator);
776 Enumerator = NULL;
779 if(--deviceCount == 0 && SUCCEEDED(cohr))
780 CoUninitialize();
782 req->result = S_OK;
783 SetEvent(req->FinishedEvt);
784 continue;
786 default:
787 ERR("Unexpected message: %u\n", msg.message);
788 continue;
791 TRACE("Message loop finished\n");
793 return 0;
797 static BOOL MMDevApiLoad(void)
799 static HRESULT InitResult;
800 if(!ThreadHdl)
802 ThreadRequest req;
803 InitResult = E_FAIL;
805 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
806 if(req.FinishedEvt == NULL)
807 ERR("Failed to create event: %lu\n", GetLastError());
808 else
810 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
811 if(ThreadHdl != NULL)
812 InitResult = WaitForResponse(&req);
813 CloseHandle(req.FinishedEvt);
816 return SUCCEEDED(InitResult);
820 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
822 MMDevApiData *data = NULL;
823 HRESULT hr;
825 //Initialise requested device
826 data = calloc(1, sizeof(MMDevApiData));
827 if(!data)
828 return ALC_OUT_OF_MEMORY;
829 device->ExtraData = data;
831 hr = S_OK;
832 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
833 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
834 if(data->NotifyEvent == NULL || data->MsgEvent == NULL)
835 hr = E_FAIL;
837 if(SUCCEEDED(hr))
839 if(deviceName)
841 ALuint i;
843 if(!PlaybackDeviceList)
845 ThreadRequest req = { data->MsgEvent, 0 };
846 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
847 (void)WaitForResponse(&req);
850 hr = E_FAIL;
851 for(i = 0;i < NumPlaybackDevices;i++)
853 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
855 data->devid = strdupW(PlaybackDeviceList[i].devid);
856 hr = S_OK;
857 break;
863 if(SUCCEEDED(hr))
865 ThreadRequest req = { data->MsgEvent, 0 };
867 hr = E_FAIL;
868 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
869 hr = WaitForResponse(&req);
872 if(FAILED(hr))
874 if(data->NotifyEvent != NULL)
875 CloseHandle(data->NotifyEvent);
876 data->NotifyEvent = NULL;
877 if(data->MsgEvent != NULL)
878 CloseHandle(data->MsgEvent);
879 data->MsgEvent = NULL;
881 free(data);
882 device->ExtraData = NULL;
884 ERR("Device init failed: 0x%08lx\n", hr);
885 return ALC_INVALID_VALUE;
888 return ALC_NO_ERROR;
891 static void MMDevApiClosePlayback(ALCdevice *device)
893 MMDevApiData *data = device->ExtraData;
894 ThreadRequest req = { data->MsgEvent, 0 };
896 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
897 (void)WaitForResponse(&req);
899 CloseHandle(data->MsgEvent);
900 data->MsgEvent = NULL;
902 CloseHandle(data->NotifyEvent);
903 data->NotifyEvent = NULL;
905 free(data->devid);
906 data->devid = NULL;
908 free(data);
909 device->ExtraData = NULL;
912 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
914 MMDevApiData *data = device->ExtraData;
915 ThreadRequest req = { data->MsgEvent, 0 };
916 HRESULT hr = E_FAIL;
918 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
919 hr = WaitForResponse(&req);
921 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
924 static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
926 MMDevApiData *data = device->ExtraData;
927 ThreadRequest req = { data->MsgEvent, 0 };
928 HRESULT hr = E_FAIL;
930 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
931 hr = WaitForResponse(&req);
933 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
936 static void MMDevApiStopPlayback(ALCdevice *device)
938 MMDevApiData *data = device->ExtraData;
939 ThreadRequest req = { data->MsgEvent, 0 };
941 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
942 (void)WaitForResponse(&req);
946 static ALint64 MMDevApiGetLatency(ALCdevice *device)
948 MMDevApiData *data = device->ExtraData;
950 return (ALint64)data->Padding * 1000000000 / device->Frequency;
954 static const BackendFuncs MMDevApiFuncs = {
955 MMDevApiOpenPlayback,
956 MMDevApiClosePlayback,
957 MMDevApiResetPlayback,
958 MMDevApiStartPlayback,
959 MMDevApiStopPlayback,
960 NULL,
961 NULL,
962 NULL,
963 NULL,
964 NULL,
965 NULL,
966 ALCdevice_LockDefault,
967 ALCdevice_UnlockDefault,
968 MMDevApiGetLatency
972 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
974 if(!MMDevApiLoad())
975 return ALC_FALSE;
976 *FuncList = MMDevApiFuncs;
977 return ALC_TRUE;
980 void alcMMDevApiDeinit(void)
982 ALuint i;
984 for(i = 0;i < NumPlaybackDevices;i++)
986 free(PlaybackDeviceList[i].name);
987 free(PlaybackDeviceList[i].devid);
989 free(PlaybackDeviceList);
990 PlaybackDeviceList = NULL;
991 NumPlaybackDevices = 0;
993 for(i = 0;i < NumCaptureDevices;i++)
995 free(CaptureDeviceList[i].name);
996 free(CaptureDeviceList[i].devid);
998 free(CaptureDeviceList);
999 CaptureDeviceList = NULL;
1000 NumCaptureDevices = 0;
1002 if(ThreadHdl)
1004 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
1005 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
1006 CloseHandle(ThreadHdl);
1007 ThreadHdl = NULL;
1011 void alcMMDevApiProbe(enum DevProbe type)
1013 ThreadRequest req = { NULL, 0 };
1014 HRESULT hr = E_FAIL;
1016 switch(type)
1018 case ALL_DEVICE_PROBE:
1019 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
1020 if(req.FinishedEvt == NULL)
1021 ERR("Failed to create event: %lu\n", GetLastError());
1022 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
1023 hr = WaitForResponse(&req);
1024 if(SUCCEEDED(hr))
1026 ALuint i;
1027 for(i = 0;i < NumPlaybackDevices;i++)
1029 if(PlaybackDeviceList[i].name)
1030 AppendAllDevicesList(PlaybackDeviceList[i].name);
1033 break;
1035 case CAPTURE_DEVICE_PROBE:
1036 break;
1038 if(req.FinishedEvt != NULL)
1039 CloseHandle(req.FinishedEvt);
1040 req.FinishedEvt = NULL;