Try to ensure at least 2 mmdevapi updates
[openal-soft.git] / Alc / backends / mmdevapi.c
blob6d54a4ab2327ab5289bcab3ba28e214567472099
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 <mmreg.h>
32 #include <propsys.h>
33 #include <propkey.h>
34 #include <devpkey.h>
35 #ifndef _WAVEFORMATEXTENSIBLE_
36 #include <ks.h>
37 #include <ksmedia.h>
38 #endif
40 #include "alMain.h"
41 #include "AL/al.h"
42 #include "AL/alc.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 MONO SPEAKER_FRONT_CENTER
49 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
50 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
51 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
52 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
53 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
54 #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)
57 typedef struct {
58 WCHAR *devid;
60 IMMDevice *mmdev;
61 IAudioClient *client;
62 IAudioRenderClient *render;
63 HANDLE hNotifyEvent;
65 HANDLE MsgEvent;
67 volatile int killNow;
68 ALvoid *thread;
69 } MMDevApiData;
72 typedef struct {
73 ALCchar *name;
74 WCHAR *devid;
75 } DevMap;
77 static DevMap *PlaybackDeviceList;
78 static ALuint NumPlaybackDevices;
79 static DevMap *CaptureDeviceList;
80 static ALuint NumCaptureDevices;
83 static HANDLE ThreadHdl;
84 static DWORD ThreadID;
86 typedef struct {
87 HANDLE FinishedEvt;
88 HRESULT result;
89 } ThreadRequest;
91 #define WM_USER_OpenDevice (WM_USER+0)
92 #define WM_USER_ResetDevice (WM_USER+1)
93 #define WM_USER_StartDevice (WM_USER+2)
94 #define WM_USER_StopDevice (WM_USER+3)
95 #define WM_USER_CloseDevice (WM_USER+4)
96 #define WM_USER_Enumerate (WM_USER+5)
98 static HRESULT WaitForResponse(ThreadRequest *req)
100 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
101 return req->result;
102 ERR("Message response error: %lu\n", GetLastError());
103 return E_FAIL;
107 static ALCchar *get_device_name(IMMDevice *device)
109 ALCchar *name = NULL;
110 IPropertyStore *ps;
111 PROPVARIANT pvname;
112 HRESULT hr;
113 int len;
115 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
116 if(FAILED(hr))
118 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
119 return calloc(1, 1);
122 PropVariantInit(&pvname);
124 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
125 if(FAILED(hr))
127 WARN("GetValue failed: 0x%08lx\n", hr);
128 name = calloc(1, 1);
130 else
132 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
134 name = calloc(1, len);
135 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL);
139 PropVariantClear(&pvname);
140 IPropertyStore_Release(ps);
142 return name;
145 static void add_device(IMMDevice *device, DevMap *devmap)
147 LPWSTR devid;
148 HRESULT hr;
150 hr = IMMDevice_GetId(device, &devid);
151 if(SUCCEEDED(hr))
153 devmap->devid = strdupW(devid);
154 devmap->name = get_device_name(device);
155 TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid);
156 CoTaskMemFree(devid);
160 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
162 IMMDeviceCollection *coll;
163 IMMDevice *defdev = NULL;
164 DevMap *devlist = NULL;
165 HRESULT hr;
166 UINT count;
167 UINT idx;
168 UINT i;
170 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
171 if(FAILED(hr))
173 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
174 return NULL;
177 idx = count = 0;
178 hr = IMMDeviceCollection_GetCount(coll, &count);
179 if(SUCCEEDED(hr) && count > 0)
181 devlist = calloc(count, sizeof(*devlist));
182 if(!devlist)
184 IMMDeviceCollection_Release(coll);
185 return NULL;
188 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
189 eMultimedia, &defdev);
191 if(SUCCEEDED(hr) && defdev != NULL)
192 add_device(defdev, &devlist[idx++]);
194 for(i = 0;i < count && idx < count;++i)
196 IMMDevice *device;
198 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
199 continue;
201 if(device != defdev)
202 add_device(device, &devlist[idx++]);
204 IMMDevice_Release(device);
207 if(defdev) IMMDevice_Release(defdev);
208 IMMDeviceCollection_Release(coll);
210 *numdevs = idx;
211 return devlist;
215 static ALuint MMDevApiProc(ALvoid *ptr)
217 ALCdevice *device = ptr;
218 MMDevApiData *data = device->ExtraData;
219 UINT32 update_size, num_updates;
220 UINT32 written, len;
221 BYTE *buffer;
222 HRESULT hr;
224 hr = CoInitialize(NULL);
225 if(FAILED(hr))
227 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
228 aluHandleDisconnect(device);
229 return 0;
232 SetRTPriority();
234 update_size = device->UpdateSize;
235 num_updates = device->NumUpdates;
236 while(!data->killNow)
238 hr = IAudioClient_GetCurrentPadding(data->client, &written);
239 if(FAILED(hr))
241 ERR("Failed to get padding: 0x%08lx\n", hr);
242 aluHandleDisconnect(device);
243 break;
246 len = update_size*num_updates - written;
247 if(len < update_size)
249 DWORD res;
250 res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
251 if(res != WAIT_OBJECT_0)
252 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
253 continue;
255 len -= len%update_size;
257 hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer);
258 if(SUCCEEDED(hr))
260 aluMixData(device, buffer, len);
261 hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
263 if(FAILED(hr))
265 ERR("Failed to buffer data: 0x%08lx\n", hr);
266 aluHandleDisconnect(device);
267 break;
271 CoUninitialize();
272 return 0;
276 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
278 memset(out, 0, sizeof(*out));
279 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
280 *out = *(const WAVEFORMATEXTENSIBLE*)in;
281 else if(in->wFormatTag == WAVE_FORMAT_PCM)
283 out->Format = *in;
284 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
285 out->Format.cbSize = sizeof(*out) - sizeof(*in);
286 if(out->Format.nChannels == 1)
287 out->dwChannelMask = MONO;
288 else if(out->Format.nChannels == 2)
289 out->dwChannelMask = STEREO;
290 else
291 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
292 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
294 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
296 out->Format = *in;
297 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
298 out->Format.cbSize = sizeof(*out) - sizeof(*in);
299 if(out->Format.nChannels == 1)
300 out->dwChannelMask = MONO;
301 else if(out->Format.nChannels == 2)
302 out->dwChannelMask = STEREO;
303 else
304 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
305 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
307 else
309 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
310 return ALC_FALSE;
312 return ALC_TRUE;
315 static HRESULT DoReset(ALCdevice *device)
317 MMDevApiData *data = device->ExtraData;
318 WAVEFORMATEXTENSIBLE OutputType;
319 WAVEFORMATEX *wfx = NULL;
320 REFERENCE_TIME min_per, buf_time;
321 UINT32 buffer_len, min_len;
322 HRESULT hr;
324 hr = IAudioClient_GetMixFormat(data->client, &wfx);
325 if(FAILED(hr))
327 ERR("Failed to get mix format: 0x%08lx\n", hr);
328 return hr;
331 if(!MakeExtensible(&OutputType, wfx))
333 CoTaskMemFree(wfx);
334 return E_FAIL;
336 CoTaskMemFree(wfx);
337 wfx = NULL;
339 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
340 device->Frequency-1) / device->Frequency;
342 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
343 device->Frequency = OutputType.Format.nSamplesPerSec;
344 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
346 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
347 device->FmtChans = DevFmtMono;
348 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
349 device->FmtChans = DevFmtStereo;
350 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
351 device->FmtChans = DevFmtQuad;
352 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
353 device->FmtChans = DevFmtX51;
354 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
355 device->FmtChans = DevFmtX51Side;
356 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
357 device->FmtChans = DevFmtX61;
358 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
359 device->FmtChans = DevFmtX71;
360 else
361 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
364 switch(device->FmtChans)
366 case DevFmtMono:
367 OutputType.Format.nChannels = 1;
368 OutputType.dwChannelMask = MONO;
369 break;
370 case DevFmtStereo:
371 OutputType.Format.nChannels = 2;
372 OutputType.dwChannelMask = STEREO;
373 break;
374 case DevFmtQuad:
375 OutputType.Format.nChannels = 4;
376 OutputType.dwChannelMask = QUAD;
377 break;
378 case DevFmtX51:
379 OutputType.Format.nChannels = 6;
380 OutputType.dwChannelMask = X5DOT1;
381 break;
382 case DevFmtX51Side:
383 OutputType.Format.nChannels = 6;
384 OutputType.dwChannelMask = X5DOT1SIDE;
385 break;
386 case DevFmtX61:
387 OutputType.Format.nChannels = 7;
388 OutputType.dwChannelMask = X6DOT1;
389 break;
390 case DevFmtX71:
391 OutputType.Format.nChannels = 8;
392 OutputType.dwChannelMask = X7DOT1;
393 break;
395 switch(device->FmtType)
397 case DevFmtByte:
398 device->FmtType = DevFmtUByte;
399 /* fall-through */
400 case DevFmtUByte:
401 OutputType.Format.wBitsPerSample = 8;
402 OutputType.Samples.wValidBitsPerSample = 8;
403 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
404 break;
405 case DevFmtUShort:
406 device->FmtType = DevFmtShort;
407 /* fall-through */
408 case DevFmtShort:
409 OutputType.Format.wBitsPerSample = 16;
410 OutputType.Samples.wValidBitsPerSample = 16;
411 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
412 break;
413 case DevFmtUInt:
414 device->FmtType = DevFmtInt;
415 /* fall-through */
416 case DevFmtInt:
417 OutputType.Format.wBitsPerSample = 32;
418 OutputType.Samples.wValidBitsPerSample = 32;
419 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
420 break;
421 case DevFmtFloat:
422 OutputType.Format.wBitsPerSample = 32;
423 OutputType.Samples.wValidBitsPerSample = 32;
424 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
425 break;
427 OutputType.Format.nSamplesPerSec = device->Frequency;
429 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
430 OutputType.Format.wBitsPerSample / 8;
431 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
432 OutputType.Format.nBlockAlign;
434 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
435 if(FAILED(hr))
437 ERR("Failed to check format support: 0x%08lx\n", hr);
438 hr = IAudioClient_GetMixFormat(data->client, &wfx);
440 if(FAILED(hr))
442 ERR("Failed to find a supported format: 0x%08lx\n", hr);
443 return hr;
446 if(wfx != NULL)
448 if(!MakeExtensible(&OutputType, wfx))
450 CoTaskMemFree(wfx);
451 return E_FAIL;
453 CoTaskMemFree(wfx);
454 wfx = NULL;
456 device->Frequency = OutputType.Format.nSamplesPerSec;
457 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
458 device->FmtChans = DevFmtMono;
459 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
460 device->FmtChans = DevFmtStereo;
461 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
462 device->FmtChans = DevFmtQuad;
463 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
464 device->FmtChans = DevFmtX51;
465 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
466 device->FmtChans = DevFmtX51Side;
467 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
468 device->FmtChans = DevFmtX61;
469 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
470 device->FmtChans = DevFmtX71;
471 else
473 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
474 device->FmtChans = DevFmtStereo;
475 OutputType.Format.nChannels = 2;
476 OutputType.dwChannelMask = STEREO;
479 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
481 if(OutputType.Format.wBitsPerSample == 8)
482 device->FmtType = DevFmtUByte;
483 else if(OutputType.Format.wBitsPerSample == 16)
484 device->FmtType = DevFmtShort;
485 else if(OutputType.Format.wBitsPerSample == 32)
486 device->FmtType = DevFmtInt;
487 else
489 device->FmtType = DevFmtShort;
490 OutputType.Format.wBitsPerSample = 16;
493 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
495 device->FmtType = DevFmtFloat;
496 OutputType.Format.wBitsPerSample = 32;
498 else
500 ERR("Unhandled format sub-type\n");
501 device->FmtType = DevFmtShort;
502 OutputType.Format.wBitsPerSample = 16;
503 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
505 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
508 SetDefaultWFXChannelOrder(device);
510 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
511 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
512 buf_time, 0, &OutputType.Format, NULL);
513 if(FAILED(hr))
515 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
516 return hr;
519 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
520 if(SUCCEEDED(hr))
522 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
523 /* Find the nearest multiple of the period size to the update size */
524 if(min_len < device->UpdateSize)
525 min_len *= (device->UpdateSize + min_len/2)/min_len;
526 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
528 if(FAILED(hr))
530 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
531 return hr;
534 device->UpdateSize = min_len;
535 device->NumUpdates = buffer_len / device->UpdateSize;
536 if(device->NumUpdates <= 1)
538 ERR("Audio client returned buffer_len < period*2; expect break up\n");
539 device->NumUpdates = 2;
540 device->UpdateSize = buffer_len / device->NumUpdates;
543 return hr;
547 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
549 ThreadRequest *req = ptr;
550 IMMDeviceEnumerator *Enumerator;
551 ALuint deviceCount = 0;
552 MMDevApiData *data;
553 ALCdevice *device;
554 HRESULT hr, cohr;
555 MSG msg;
557 TRACE("Starting message thread\n");
559 cohr = CoInitialize(NULL);
560 if(FAILED(cohr))
562 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
563 req->result = cohr;
564 SetEvent(req->FinishedEvt);
565 return 0;
568 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
569 if(FAILED(hr))
571 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
572 CoUninitialize();
573 req->result = hr;
574 SetEvent(req->FinishedEvt);
575 return 0;
577 Enumerator = ptr;
578 IMMDeviceEnumerator_Release(Enumerator);
579 Enumerator = NULL;
581 CoUninitialize();
583 req->result = S_OK;
584 SetEvent(req->FinishedEvt);
586 TRACE("Starting message loop\n");
587 while(GetMessage(&msg, NULL, 0, 0))
589 TRACE("Got message %u\n", msg.message);
590 switch(msg.message)
592 case WM_USER_OpenDevice:
593 req = (ThreadRequest*)msg.wParam;
594 device = (ALCdevice*)msg.lParam;
595 data = device->ExtraData;
597 hr = cohr = S_OK;
598 if(++deviceCount == 1)
599 hr = cohr = CoInitialize(NULL);
600 if(SUCCEEDED(hr))
601 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
602 if(SUCCEEDED(hr))
604 Enumerator = ptr;
605 if(!data->devid)
606 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
607 else
608 hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
609 IMMDeviceEnumerator_Release(Enumerator);
610 Enumerator = NULL;
612 if(SUCCEEDED(hr))
613 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
614 if(SUCCEEDED(hr))
616 data->client = ptr;
617 device->szDeviceName = get_device_name(data->mmdev);
620 if(FAILED(hr))
622 if(data->mmdev)
623 IMMDevice_Release(data->mmdev);
624 data->mmdev = NULL;
625 if(--deviceCount == 0 && SUCCEEDED(cohr))
626 CoUninitialize();
629 req->result = hr;
630 SetEvent(req->FinishedEvt);
631 continue;
633 case WM_USER_ResetDevice:
634 req = (ThreadRequest*)msg.wParam;
635 device = (ALCdevice*)msg.lParam;
637 req->result = DoReset(device);
638 SetEvent(req->FinishedEvt);
639 continue;
641 case WM_USER_StartDevice:
642 req = (ThreadRequest*)msg.wParam;
643 device = (ALCdevice*)msg.lParam;
644 data = device->ExtraData;
646 ResetEvent(data->hNotifyEvent);
647 hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
648 if(FAILED(hr))
649 ERR("Failed to set event handle: 0x%08lx\n", hr);
650 else
652 hr = IAudioClient_Start(data->client);
653 if(FAILED(hr))
654 ERR("Failed to start audio client: 0x%08lx\n", hr);
657 if(SUCCEEDED(hr))
658 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
659 if(SUCCEEDED(hr))
661 data->render = ptr;
662 data->thread = StartThread(MMDevApiProc, device);
663 if(!data->thread)
665 if(data->render)
666 IAudioRenderClient_Release(data->render);
667 data->render = NULL;
668 IAudioClient_Stop(data->client);
669 ERR("Failed to start thread\n");
670 hr = E_FAIL;
674 req->result = hr;
675 SetEvent(req->FinishedEvt);
676 continue;
678 case WM_USER_StopDevice:
679 req = (ThreadRequest*)msg.wParam;
680 device = (ALCdevice*)msg.lParam;
681 data = device->ExtraData;
683 if(data->thread)
685 data->killNow = 1;
686 StopThread(data->thread);
687 data->thread = NULL;
689 data->killNow = 0;
691 IAudioRenderClient_Release(data->render);
692 data->render = NULL;
693 IAudioClient_Stop(data->client);
696 req->result = S_OK;
697 SetEvent(req->FinishedEvt);
698 continue;
700 case WM_USER_CloseDevice:
701 req = (ThreadRequest*)msg.wParam;
702 device = (ALCdevice*)msg.lParam;
703 data = device->ExtraData;
705 IAudioClient_Release(data->client);
706 data->client = NULL;
708 IMMDevice_Release(data->mmdev);
709 data->mmdev = NULL;
711 if(--deviceCount == 0)
712 CoUninitialize();
714 req->result = S_OK;
715 SetEvent(req->FinishedEvt);
716 continue;
718 case WM_USER_Enumerate:
719 req = (ThreadRequest*)msg.wParam;
721 hr = cohr = S_OK;
722 if(++deviceCount == 1)
723 hr = cohr = CoInitialize(NULL);
724 if(SUCCEEDED(hr))
725 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
726 if(SUCCEEDED(hr))
728 EDataFlow flowdir;
729 DevMap **devlist;
730 ALuint *numdevs;
731 ALuint i;
733 Enumerator = ptr;
734 if(msg.lParam == CAPTURE_DEVICE_PROBE)
736 flowdir = eCapture;
737 devlist = &CaptureDeviceList;
738 numdevs = &NumCaptureDevices;
740 else
742 flowdir = eRender;
743 devlist = &PlaybackDeviceList;
744 numdevs = &NumPlaybackDevices;
747 for(i = 0;i < *numdevs;i++)
749 free((*devlist)[i].name);
750 free((*devlist)[i].devid);
752 free(*devlist);
753 *devlist = NULL;
754 *numdevs = 0;
756 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
758 IMMDeviceEnumerator_Release(Enumerator);
759 Enumerator = NULL;
762 if(--deviceCount == 0 && SUCCEEDED(cohr))
763 CoUninitialize();
765 req->result = S_OK;
766 SetEvent(req->FinishedEvt);
767 continue;
769 default:
770 ERR("Unexpected message: %u\n", msg.message);
771 continue;
774 TRACE("Message loop finished\n");
776 return 0;
780 static BOOL MMDevApiLoad(void)
782 static HRESULT InitResult;
783 if(!ThreadHdl)
785 ThreadRequest req;
786 InitResult = E_FAIL;
788 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
789 if(req.FinishedEvt == NULL)
790 ERR("Failed to create event: %lu\n", GetLastError());
791 else
793 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
794 if(ThreadHdl != NULL)
795 InitResult = WaitForResponse(&req);
796 CloseHandle(req.FinishedEvt);
799 return SUCCEEDED(InitResult);
803 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
805 MMDevApiData *data = NULL;
806 HRESULT hr;
808 //Initialise requested device
809 data = calloc(1, sizeof(MMDevApiData));
810 if(!data)
811 return ALC_OUT_OF_MEMORY;
812 device->ExtraData = data;
814 hr = S_OK;
815 data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
816 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
817 if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
818 hr = E_FAIL;
820 if(SUCCEEDED(hr))
822 if(deviceName)
824 ALuint i;
826 if(!PlaybackDeviceList)
828 ThreadRequest req = { data->MsgEvent, 0 };
829 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
830 (void)WaitForResponse(&req);
833 hr = E_FAIL;
834 for(i = 0;i < NumPlaybackDevices;i++)
836 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
838 data->devid = strdupW(PlaybackDeviceList[i].devid);
839 hr = S_OK;
840 break;
846 if(SUCCEEDED(hr))
848 ThreadRequest req = { data->MsgEvent, 0 };
850 hr = E_FAIL;
851 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
852 hr = WaitForResponse(&req);
855 if(FAILED(hr))
857 if(data->hNotifyEvent != NULL)
858 CloseHandle(data->hNotifyEvent);
859 data->hNotifyEvent = NULL;
860 if(data->MsgEvent != NULL)
861 CloseHandle(data->MsgEvent);
862 data->MsgEvent = NULL;
864 free(data);
865 device->ExtraData = NULL;
867 ERR("Device init failed: 0x%08lx\n", hr);
868 return ALC_INVALID_VALUE;
871 return ALC_NO_ERROR;
874 static void MMDevApiClosePlayback(ALCdevice *device)
876 MMDevApiData *data = device->ExtraData;
877 ThreadRequest req = { data->MsgEvent, 0 };
879 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
880 (void)WaitForResponse(&req);
882 CloseHandle(data->MsgEvent);
883 data->MsgEvent = NULL;
885 CloseHandle(data->hNotifyEvent);
886 data->hNotifyEvent = NULL;
888 free(data->devid);
889 data->devid = NULL;
891 free(data);
892 device->ExtraData = NULL;
895 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
897 MMDevApiData *data = device->ExtraData;
898 ThreadRequest req = { data->MsgEvent, 0 };
899 HRESULT hr = E_FAIL;
901 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
902 hr = WaitForResponse(&req);
904 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
907 static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
909 MMDevApiData *data = device->ExtraData;
910 ThreadRequest req = { data->MsgEvent, 0 };
911 HRESULT hr = E_FAIL;
913 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
914 hr = WaitForResponse(&req);
916 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
919 static void MMDevApiStopPlayback(ALCdevice *device)
921 MMDevApiData *data = device->ExtraData;
922 ThreadRequest req = { data->MsgEvent, 0 };
924 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
925 (void)WaitForResponse(&req);
929 static const BackendFuncs MMDevApiFuncs = {
930 MMDevApiOpenPlayback,
931 MMDevApiClosePlayback,
932 MMDevApiResetPlayback,
933 MMDevApiStartPlayback,
934 MMDevApiStopPlayback,
935 NULL,
936 NULL,
937 NULL,
938 NULL,
939 NULL,
940 NULL
944 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
946 if(!MMDevApiLoad())
947 return ALC_FALSE;
948 *FuncList = MMDevApiFuncs;
949 return ALC_TRUE;
952 void alcMMDevApiDeinit(void)
954 ALuint i;
956 for(i = 0;i < NumPlaybackDevices;i++)
958 free(PlaybackDeviceList[i].name);
959 free(PlaybackDeviceList[i].devid);
961 free(PlaybackDeviceList);
962 PlaybackDeviceList = NULL;
963 NumPlaybackDevices = 0;
965 for(i = 0;i < NumCaptureDevices;i++)
967 free(CaptureDeviceList[i].name);
968 free(CaptureDeviceList[i].devid);
970 free(CaptureDeviceList);
971 CaptureDeviceList = NULL;
972 NumCaptureDevices = 0;
974 if(ThreadHdl)
976 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
977 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
978 CloseHandle(ThreadHdl);
979 ThreadHdl = NULL;
983 void alcMMDevApiProbe(enum DevProbe type)
985 ThreadRequest req = { NULL, 0 };
986 HRESULT hr = E_FAIL;
988 switch(type)
990 case ALL_DEVICE_PROBE:
991 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
992 if(req.FinishedEvt == NULL)
993 ERR("Failed to create event: %lu\n", GetLastError());
994 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
995 hr = WaitForResponse(&req);
996 if(SUCCEEDED(hr))
998 ALuint i;
999 for(i = 0;i < NumPlaybackDevices;i++)
1001 if(PlaybackDeviceList[i].name)
1002 AppendAllDeviceList(PlaybackDeviceList[i].name);
1005 break;
1007 case CAPTURE_DEVICE_PROBE:
1008 break;
1010 if(req.FinishedEvt != NULL)
1011 CloseHandle(req.FinishedEvt);
1012 req.FinishedEvt = NULL;