Initialize a variable before use
[openal-soft.git] / Alc / backends / mmdevapi.c
blob86c174d1d662803831df3bd0d3c37929d3cf3ba9
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 GUID guid;
59 IMMDevice *mmdev;
60 IAudioClient *client;
61 HANDLE hNotifyEvent;
63 HANDLE MsgEvent;
65 volatile int killNow;
66 ALvoid *thread;
67 } MMDevApiData;
70 typedef struct {
71 ALCchar *name;
72 GUID guid;
73 } DevMap;
75 static const ALCchar mmDevice[] = "WASAPI Default";
76 static DevMap *PlaybackDeviceList;
77 static ALuint NumPlaybackDevices;
78 static DevMap *CaptureDeviceList;
79 static ALuint NumCaptureDevices;
82 static HANDLE ThreadHdl;
83 static DWORD ThreadID;
85 typedef struct {
86 HANDLE FinishedEvt;
87 HRESULT result;
88 } ThreadRequest;
90 #define WM_USER_OpenDevice (WM_USER+0)
91 #define WM_USER_ResetDevice (WM_USER+1)
92 #define WM_USER_StopDevice (WM_USER+2)
93 #define WM_USER_CloseDevice (WM_USER+3)
94 #define WM_USER_Enumerate (WM_USER+4)
96 static HRESULT WaitForResponse(ThreadRequest *req)
98 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
99 return req->result;
100 ERR("Message response error: %lu\n", GetLastError());
101 return E_FAIL;
105 static HRESULT get_mmdevice_by_guid(IMMDeviceEnumerator *devenum, EDataFlow flowdir, const GUID *guid, IMMDevice **out)
107 IMMDeviceCollection *coll;
108 DWORD count, i;
109 HRESULT hr;
111 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
112 if(FAILED(hr))
114 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
115 return hr;
118 count = 0;
119 IMMDeviceCollection_GetCount(coll, &count);
120 for(i = 0;i < count;++i)
122 IMMDevice *device;
123 IPropertyStore *ps;
124 PROPVARIANT pv;
125 GUID devid;
127 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
128 continue;
130 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
131 if(FAILED(hr))
133 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
134 continue;
137 PropVariantInit(&pv);
139 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pv);
140 if(FAILED(hr))
142 PropVariantClear(&pv);
143 IPropertyStore_Release(ps);
144 WARN("GetValue failed: 0x%08lx\n", hr);
145 continue;
147 CLSIDFromString(pv.pwszVal, &devid);
149 PropVariantClear(&pv);
150 IPropertyStore_Release(ps);
152 if(IsEqualGUID(&devid, guid))
154 *out = device;
155 break;
158 IMMDevice_Release(device);
160 hr = ((i==count) ? E_FAIL : S_OK);
162 IMMDeviceCollection_Release(coll);
163 return hr;
167 static void add_device(IMMDevice *device, DevMap *devmap)
169 IPropertyStore *ps;
170 PROPVARIANT pvguid;
171 PROPVARIANT pvname;
172 HRESULT hr;
173 int len;
175 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
176 if(FAILED(hr))
178 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
179 return;
182 PropVariantInit(&pvguid);
183 PropVariantInit(&pvname);
185 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pvguid);
186 if(FAILED(hr))
187 WARN("GetValue failed: 0x%08lx\n", hr);
188 else
190 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
191 if(FAILED(hr))
192 WARN("GetValue failed: 0x%08lx\n", hr);
194 if(SUCCEEDED(hr))
196 CLSIDFromString(pvguid.pwszVal, &devmap->guid);
197 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
199 devmap->name = calloc(1, len);
200 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, devmap->name, len, NULL, NULL);
204 PropVariantClear(&pvname);
205 PropVariantClear(&pvguid);
206 IPropertyStore_Release(ps);
209 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
211 IMMDeviceCollection *coll;
212 IMMDevice *defdev = NULL;
213 DevMap *devlist;
214 DWORD count;
215 HRESULT hr;
216 DWORD idx;
217 DWORD i;
219 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
220 if(FAILED(hr))
222 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
223 return NULL;
226 idx = count = 0;
227 hr = IMMDeviceCollection_GetCount(coll, &count);
228 if(SUCCEEDED(hr) && count > 0)
230 devlist = calloc(count, sizeof(*devlist));
231 if(!devlist)
233 IMMDeviceCollection_Release(coll);
234 return NULL;
237 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
238 eMultimedia, &defdev);
240 if(SUCCEEDED(hr) && defdev != NULL)
241 add_device(defdev, &devlist[idx++]);
243 for(i = 0;i < count && idx < count;++i)
245 IMMDevice *device;
247 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
248 continue;
250 if(device != defdev)
251 add_device(device, &devlist[idx++]);
253 IMMDevice_Release(device);
256 if(defdev) IMMDevice_Release(defdev);
257 IMMDeviceCollection_Release(coll);
259 *numdevs = idx;
260 return devlist;
264 static ALuint MMDevApiProc(ALvoid *ptr)
266 ALCdevice *device = ptr;
267 MMDevApiData *data = device->ExtraData;
268 union {
269 IAudioRenderClient *iface;
270 void *ptr;
271 } render;
272 UINT32 written, len;
273 BYTE *buffer;
274 HRESULT hr;
276 hr = CoInitialize(NULL);
277 if(FAILED(hr))
279 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
280 aluHandleDisconnect(device);
281 return 0;
284 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr);
285 if(FAILED(hr))
287 ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr);
288 aluHandleDisconnect(device);
289 return 0;
292 SetRTPriority();
294 while(!data->killNow)
296 hr = IAudioClient_GetCurrentPadding(data->client, &written);
297 if(FAILED(hr))
299 ERR("Failed to get padding: 0x%08lx\n", hr);
300 aluHandleDisconnect(device);
301 break;
304 len = device->UpdateSize*device->NumUpdates - written;
305 if(len < device->UpdateSize)
307 DWORD res;
308 res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
309 if(res != WAIT_OBJECT_0)
310 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
311 continue;
313 len -= len%device->UpdateSize;
315 hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer);
316 if(SUCCEEDED(hr))
318 aluMixData(device, buffer, len);
319 hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0);
321 if(FAILED(hr))
323 ERR("Failed to buffer data: 0x%08lx\n", hr);
324 aluHandleDisconnect(device);
325 break;
329 IAudioRenderClient_Release(render.iface);
331 CoUninitialize();
332 return 0;
336 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
338 memset(out, 0, sizeof(*out));
339 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
340 *out = *(WAVEFORMATEXTENSIBLE*)in;
341 else if(in->wFormatTag == WAVE_FORMAT_PCM)
343 out->Format = *in;
344 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
345 out->Format.cbSize = sizeof(*out) - sizeof(*in);
346 if(out->Format.nChannels == 1)
347 out->dwChannelMask = MONO;
348 else if(out->Format.nChannels == 2)
349 out->dwChannelMask = STEREO;
350 else
351 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
352 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
354 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
356 out->Format = *in;
357 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
358 out->Format.cbSize = sizeof(*out) - sizeof(*in);
359 if(out->Format.nChannels == 1)
360 out->dwChannelMask = MONO;
361 else if(out->Format.nChannels == 2)
362 out->dwChannelMask = STEREO;
363 else
364 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
365 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
367 else
369 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
370 return ALC_FALSE;
372 return ALC_TRUE;
375 static HRESULT DoReset(ALCdevice *device)
377 MMDevApiData *data = device->ExtraData;
378 WAVEFORMATEXTENSIBLE OutputType;
379 WAVEFORMATEX *wfx = NULL;
380 REFERENCE_TIME min_per, buf_time;
381 UINT32 buffer_len, min_len;
382 HRESULT hr;
384 hr = IAudioClient_GetMixFormat(data->client, &wfx);
385 if(FAILED(hr))
387 ERR("Failed to get mix format: 0x%08lx\n", hr);
388 return hr;
391 if(!MakeExtensible(&OutputType, wfx))
393 CoTaskMemFree(wfx);
394 return E_FAIL;
396 CoTaskMemFree(wfx);
397 wfx = NULL;
399 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
400 device->Frequency-1) / device->Frequency;
402 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
403 device->Frequency = OutputType.Format.nSamplesPerSec;
404 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
406 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
407 device->FmtChans = DevFmtMono;
408 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
409 device->FmtChans = DevFmtStereo;
410 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
411 device->FmtChans = DevFmtQuad;
412 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
413 device->FmtChans = DevFmtX51;
414 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
415 device->FmtChans = DevFmtX51Side;
416 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
417 device->FmtChans = DevFmtX61;
418 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
419 device->FmtChans = DevFmtX71;
420 else
421 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
424 switch(device->FmtChans)
426 case DevFmtMono:
427 OutputType.Format.nChannels = 1;
428 OutputType.dwChannelMask = MONO;
429 break;
430 case DevFmtStereo:
431 OutputType.Format.nChannels = 2;
432 OutputType.dwChannelMask = STEREO;
433 break;
434 case DevFmtQuad:
435 OutputType.Format.nChannels = 4;
436 OutputType.dwChannelMask = QUAD;
437 break;
438 case DevFmtX51:
439 OutputType.Format.nChannels = 6;
440 OutputType.dwChannelMask = X5DOT1;
441 break;
442 case DevFmtX51Side:
443 OutputType.Format.nChannels = 6;
444 OutputType.dwChannelMask = X5DOT1SIDE;
445 break;
446 case DevFmtX61:
447 OutputType.Format.nChannels = 7;
448 OutputType.dwChannelMask = X6DOT1;
449 break;
450 case DevFmtX71:
451 OutputType.Format.nChannels = 8;
452 OutputType.dwChannelMask = X7DOT1;
453 break;
455 switch(device->FmtType)
457 case DevFmtByte:
458 device->FmtType = DevFmtUByte;
459 /* fall-through */
460 case DevFmtUByte:
461 OutputType.Format.wBitsPerSample = 8;
462 OutputType.Samples.wValidBitsPerSample = 8;
463 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
464 break;
465 case DevFmtUShort:
466 device->FmtType = DevFmtShort;
467 /* fall-through */
468 case DevFmtShort:
469 OutputType.Format.wBitsPerSample = 16;
470 OutputType.Samples.wValidBitsPerSample = 16;
471 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
472 break;
473 case DevFmtUInt:
474 device->FmtType = DevFmtInt;
475 /* fall-through */
476 case DevFmtInt:
477 OutputType.Format.wBitsPerSample = 32;
478 OutputType.Samples.wValidBitsPerSample = 32;
479 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
480 break;
481 case DevFmtFloat:
482 OutputType.Format.wBitsPerSample = 32;
483 OutputType.Samples.wValidBitsPerSample = 32;
484 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
485 break;
487 OutputType.Format.nSamplesPerSec = device->Frequency;
489 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
490 OutputType.Format.wBitsPerSample / 8;
491 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
492 OutputType.Format.nBlockAlign;
494 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
495 if(FAILED(hr))
497 ERR("Failed to check format support: 0x%08lx\n", hr);
498 hr = IAudioClient_GetMixFormat(data->client, &wfx);
500 if(FAILED(hr))
502 ERR("Failed to find a supported format: 0x%08lx\n", hr);
503 return hr;
506 if(wfx != NULL)
508 if(!MakeExtensible(&OutputType, wfx))
510 CoTaskMemFree(wfx);
511 return E_FAIL;
513 CoTaskMemFree(wfx);
514 wfx = NULL;
516 device->Frequency = OutputType.Format.nSamplesPerSec;
517 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
518 device->FmtChans = DevFmtMono;
519 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
520 device->FmtChans = DevFmtStereo;
521 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
522 device->FmtChans = DevFmtQuad;
523 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
524 device->FmtChans = DevFmtX51;
525 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
526 device->FmtChans = DevFmtX51Side;
527 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
528 device->FmtChans = DevFmtX61;
529 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
530 device->FmtChans = DevFmtX71;
531 else
533 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
534 device->FmtChans = DevFmtStereo;
535 OutputType.Format.nChannels = 2;
536 OutputType.dwChannelMask = STEREO;
539 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
541 if(OutputType.Format.wBitsPerSample == 8)
542 device->FmtType = DevFmtUByte;
543 else if(OutputType.Format.wBitsPerSample == 16)
544 device->FmtType = DevFmtShort;
545 else if(OutputType.Format.wBitsPerSample == 32)
546 device->FmtType = DevFmtInt;
547 else
549 device->FmtType = DevFmtShort;
550 OutputType.Format.wBitsPerSample = 16;
553 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
555 device->FmtType = DevFmtFloat;
556 OutputType.Format.wBitsPerSample = 32;
558 else
560 ERR("Unhandled format sub-type\n");
561 device->FmtType = DevFmtShort;
562 OutputType.Format.wBitsPerSample = 16;
563 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
565 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
568 SetDefaultWFXChannelOrder(device);
570 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
571 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
572 buf_time, 0, &OutputType.Format, NULL);
573 if(FAILED(hr))
575 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
576 return hr;
579 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
580 if(SUCCEEDED(hr))
582 min_len = (min_per*device->Frequency + 10000000-1) / 10000000;
583 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
585 if(FAILED(hr))
587 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
588 return hr;
591 device->UpdateSize = min_len;
592 device->NumUpdates = buffer_len / device->UpdateSize;
593 if(device->NumUpdates <= 1)
595 device->NumUpdates = 1;
596 ERR("Audio client returned buffer_len < period*2; expect break up\n");
599 ResetEvent(data->hNotifyEvent);
600 hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
601 if(SUCCEEDED(hr))
602 hr = IAudioClient_Start(data->client);
603 if(FAILED(hr))
605 ERR("Failed to start audio client: 0x%08lx\n", hr);
606 return hr;
609 data->thread = StartThread(MMDevApiProc, device);
610 if(!data->thread)
612 IAudioClient_Stop(data->client);
613 ERR("Failed to start thread\n");
614 return E_FAIL;
617 return hr;
621 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
623 ThreadRequest *req = ptr;
624 IMMDeviceEnumerator *Enumerator;
625 ALuint deviceCount = 0;
626 MMDevApiData *data;
627 ALCdevice *device;
628 HRESULT hr, cohr;
629 MSG msg;
631 TRACE("Starting message thread\n");
633 cohr = CoInitialize(NULL);
634 if(FAILED(cohr))
636 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
637 req->result = cohr;
638 SetEvent(req->FinishedEvt);
639 return 0;
642 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
643 if(FAILED(hr))
645 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
646 CoUninitialize();
647 req->result = hr;
648 SetEvent(req->FinishedEvt);
649 return 0;
651 Enumerator = ptr;
652 IMMDeviceEnumerator_Release(Enumerator);
653 Enumerator = NULL;
655 CoUninitialize();
657 req->result = S_OK;
658 SetEvent(req->FinishedEvt);
660 TRACE("Starting message loop\n");
661 while(GetMessage(&msg, NULL, 0, 0))
663 TRACE("Got message %u\n", msg.message);
664 switch(msg.message)
666 case WM_USER_OpenDevice:
667 req = (ThreadRequest*)msg.wParam;
668 device = (ALCdevice*)msg.lParam;
669 data = device->ExtraData;
671 hr = cohr = S_OK;
672 if(++deviceCount == 1)
673 hr = cohr = CoInitialize(NULL);
674 if(SUCCEEDED(hr))
675 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
676 if(SUCCEEDED(hr))
678 Enumerator = ptr;
679 if(IsEqualGUID(&data->guid, &GUID_NULL))
680 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
681 else
682 hr = get_mmdevice_by_guid(Enumerator, eRender, &data->guid, &data->mmdev);
683 IMMDeviceEnumerator_Release(Enumerator);
684 Enumerator = NULL;
686 if(SUCCEEDED(hr))
687 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
688 if(SUCCEEDED(hr))
689 data->client = ptr;
691 if(FAILED(hr))
693 if(data->mmdev)
694 IMMDevice_Release(data->mmdev);
695 data->mmdev = NULL;
696 if(--deviceCount == 0 && SUCCEEDED(cohr))
697 CoUninitialize();
700 req->result = hr;
701 SetEvent(req->FinishedEvt);
702 continue;
704 case WM_USER_ResetDevice:
705 req = (ThreadRequest*)msg.wParam;
706 device = (ALCdevice*)msg.lParam;
708 req->result = DoReset(device);
709 SetEvent(req->FinishedEvt);
710 continue;
712 case WM_USER_StopDevice:
713 req = (ThreadRequest*)msg.wParam;
714 device = (ALCdevice*)msg.lParam;
715 data = device->ExtraData;
717 if(data->thread)
719 data->killNow = 1;
720 StopThread(data->thread);
721 data->thread = NULL;
723 data->killNow = 0;
725 IAudioClient_Stop(data->client);
728 req->result = S_OK;
729 SetEvent(req->FinishedEvt);
730 continue;
732 case WM_USER_CloseDevice:
733 req = (ThreadRequest*)msg.wParam;
734 device = (ALCdevice*)msg.lParam;
735 data = device->ExtraData;
737 IAudioClient_Release(data->client);
738 data->client = NULL;
740 IMMDevice_Release(data->mmdev);
741 data->mmdev = NULL;
743 if(--deviceCount == 0)
744 CoUninitialize();
746 req->result = S_OK;
747 SetEvent(req->FinishedEvt);
748 continue;
750 case WM_USER_Enumerate:
751 req = (ThreadRequest*)msg.wParam;
753 hr = cohr = S_OK;
754 if(++deviceCount == 1)
755 hr = cohr = CoInitialize(NULL);
756 if(SUCCEEDED(hr))
757 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
758 if(SUCCEEDED(hr))
760 EDataFlow flowdir;
761 DevMap **devlist;
762 ALuint *numdevs;
763 ALuint i;
765 Enumerator = ptr;
766 if(msg.lParam == CAPTURE_DEVICE_PROBE)
768 flowdir = eCapture;
769 devlist = &CaptureDeviceList;
770 numdevs = &NumCaptureDevices;
772 else
774 flowdir = eRender;
775 devlist = &PlaybackDeviceList;
776 numdevs = &NumPlaybackDevices;
779 for(i = 0;i < *numdevs;i++)
780 free((*devlist)[i].name);
781 free(*devlist);
782 *devlist = NULL;
783 *numdevs = 0;
785 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
787 IMMDeviceEnumerator_Release(Enumerator);
788 Enumerator = NULL;
791 if(--deviceCount == 0 && SUCCEEDED(cohr))
792 CoUninitialize();
794 req->result = S_OK;
795 SetEvent(req->FinishedEvt);
796 continue;
798 default:
799 ERR("Unexpected message: %u\n", msg.message);
800 continue;
803 TRACE("Message loop finished\n");
805 return 0;
809 static BOOL MMDevApiLoad(void)
811 static HRESULT InitResult;
812 if(!ThreadHdl)
814 ThreadRequest req;
815 InitResult = E_FAIL;
817 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
818 if(req.FinishedEvt == NULL)
819 ERR("Failed to create event: %lu\n", GetLastError());
820 else
822 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
823 if(ThreadHdl != NULL)
824 InitResult = WaitForResponse(&req);
825 CloseHandle(req.FinishedEvt);
828 return SUCCEEDED(InitResult);
832 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
834 MMDevApiData *data = NULL;
835 HRESULT hr;
837 //Initialise requested device
838 data = calloc(1, sizeof(MMDevApiData));
839 if(!data)
840 return ALC_OUT_OF_MEMORY;
841 device->ExtraData = data;
843 hr = S_OK;
844 data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
845 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
846 if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
847 hr = E_FAIL;
849 if(SUCCEEDED(hr))
851 data->guid = GUID_NULL;
853 if(!deviceName)
854 deviceName = mmDevice;
855 else if(strcmp(deviceName, mmDevice) != 0)
857 ALuint i;
859 if(!PlaybackDeviceList)
861 ThreadRequest req = { data->MsgEvent, 0 };
863 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
864 (void)WaitForResponse(&req);
867 hr = E_FAIL;
868 for(i = 0;i < NumPlaybackDevices;i++)
870 if(PlaybackDeviceList[i].name &&
871 strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
873 data->guid = PlaybackDeviceList[i].guid;
874 hr = S_OK;
875 break;
881 if(SUCCEEDED(hr))
883 ThreadRequest req = { data->MsgEvent, 0 };
885 hr = E_FAIL;
886 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
887 hr = WaitForResponse(&req);
890 if(FAILED(hr))
892 if(data->hNotifyEvent != NULL)
893 CloseHandle(data->hNotifyEvent);
894 data->hNotifyEvent = NULL;
895 if(data->MsgEvent != NULL)
896 CloseHandle(data->MsgEvent);
897 data->MsgEvent = NULL;
899 free(data);
900 device->ExtraData = NULL;
902 ERR("Device init failed: 0x%08lx\n", hr);
903 return ALC_INVALID_VALUE;
906 device->szDeviceName = strdup(deviceName);
907 return ALC_NO_ERROR;
910 static void MMDevApiClosePlayback(ALCdevice *device)
912 MMDevApiData *data = device->ExtraData;
913 ThreadRequest req = { data->MsgEvent, 0 };
915 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
916 (void)WaitForResponse(&req);
918 CloseHandle(data->MsgEvent);
919 data->MsgEvent = NULL;
921 CloseHandle(data->hNotifyEvent);
922 data->hNotifyEvent = NULL;
924 free(data);
925 device->ExtraData = NULL;
928 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
930 MMDevApiData *data = device->ExtraData;
931 ThreadRequest req = { data->MsgEvent, 0 };
932 HRESULT hr = E_FAIL;
934 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
935 hr = WaitForResponse(&req);
937 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
940 static void MMDevApiStopPlayback(ALCdevice *device)
942 MMDevApiData *data = device->ExtraData;
943 ThreadRequest req = { data->MsgEvent, 0 };
945 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
946 (void)WaitForResponse(&req);
950 static const BackendFuncs MMDevApiFuncs = {
951 MMDevApiOpenPlayback,
952 MMDevApiClosePlayback,
953 MMDevApiResetPlayback,
954 MMDevApiStopPlayback,
955 NULL,
956 NULL,
957 NULL,
958 NULL,
959 NULL,
960 NULL
964 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
966 if(!MMDevApiLoad())
967 return ALC_FALSE;
968 *FuncList = MMDevApiFuncs;
969 return ALC_TRUE;
972 void alcMMDevApiDeinit(void)
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;
986 HRESULT hr = E_FAIL;
988 switch(type)
990 case DEVICE_PROBE:
991 AppendDeviceList(mmDevice);
992 break;
994 case ALL_DEVICE_PROBE:
995 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
996 if(req.FinishedEvt == NULL)
997 ERR("Failed to create event: %lu\n", GetLastError());
998 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
999 hr = WaitForResponse(&req);
1000 if(SUCCEEDED(hr))
1002 ALuint i;
1003 for(i = 0;i < NumPlaybackDevices;i++)
1005 if(PlaybackDeviceList[i].name)
1006 AppendAllDeviceList(PlaybackDeviceList[i].name);
1009 if(req.FinishedEvt != NULL)
1010 CloseHandle(req.FinishedEvt);
1011 break;
1013 case CAPTURE_DEVICE_PROBE:
1014 break;