Add DEVPKEY_Device_FriendlyName declaration for mingw-w64 compatibility
[openal-soft/openal-hmr.git] / Alc / backends / mmdevapi.c
blob0edd8e60ac439f5d35111926a44d373fd29d3e5a
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 "AL/al.h"
43 #include "AL/alc.h"
46 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
47 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
49 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
51 #define MONO SPEAKER_FRONT_CENTER
52 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
53 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
54 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
55 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
56 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
57 #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)
60 typedef struct {
61 WCHAR *devid;
63 IMMDevice *mmdev;
64 IAudioClient *client;
65 IAudioRenderClient *render;
66 HANDLE NotifyEvent;
68 HANDLE MsgEvent;
70 volatile int killNow;
71 ALvoid *thread;
72 } MMDevApiData;
75 typedef struct {
76 ALCchar *name;
77 WCHAR *devid;
78 } DevMap;
80 static DevMap *PlaybackDeviceList;
81 static ALuint NumPlaybackDevices;
82 static DevMap *CaptureDeviceList;
83 static ALuint NumCaptureDevices;
86 static HANDLE ThreadHdl;
87 static DWORD ThreadID;
89 typedef struct {
90 HANDLE FinishedEvt;
91 HRESULT result;
92 } ThreadRequest;
94 #define WM_USER_OpenDevice (WM_USER+0)
95 #define WM_USER_ResetDevice (WM_USER+1)
96 #define WM_USER_StartDevice (WM_USER+2)
97 #define WM_USER_StopDevice (WM_USER+3)
98 #define WM_USER_CloseDevice (WM_USER+4)
99 #define WM_USER_Enumerate (WM_USER+5)
101 static HRESULT WaitForResponse(ThreadRequest *req)
103 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
104 return req->result;
105 ERR("Message response error: %lu\n", GetLastError());
106 return E_FAIL;
110 static ALCchar *get_device_name(IMMDevice *device)
112 ALCchar *name = NULL;
113 IPropertyStore *ps;
114 PROPVARIANT pvname;
115 HRESULT hr;
116 int len;
118 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
119 if(FAILED(hr))
121 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
122 return calloc(1, 1);
125 PropVariantInit(&pvname);
127 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
128 if(FAILED(hr))
130 WARN("GetValue failed: 0x%08lx\n", hr);
131 name = calloc(1, 1);
133 else
135 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
137 name = calloc(1, len);
138 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL);
142 PropVariantClear(&pvname);
143 IPropertyStore_Release(ps);
145 return name;
148 static void add_device(IMMDevice *device, DevMap *devmap)
150 LPWSTR devid;
151 HRESULT hr;
153 hr = IMMDevice_GetId(device, &devid);
154 if(SUCCEEDED(hr))
156 devmap->devid = strdupW(devid);
157 devmap->name = get_device_name(device);
158 TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid);
159 CoTaskMemFree(devid);
163 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
165 IMMDeviceCollection *coll;
166 IMMDevice *defdev = NULL;
167 DevMap *devlist = NULL;
168 HRESULT hr;
169 UINT count;
170 UINT idx;
171 UINT i;
173 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
174 if(FAILED(hr))
176 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
177 return NULL;
180 idx = count = 0;
181 hr = IMMDeviceCollection_GetCount(coll, &count);
182 if(SUCCEEDED(hr) && count > 0)
184 devlist = calloc(count, sizeof(*devlist));
185 if(!devlist)
187 IMMDeviceCollection_Release(coll);
188 return NULL;
191 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
192 eMultimedia, &defdev);
194 if(SUCCEEDED(hr) && defdev != NULL)
195 add_device(defdev, &devlist[idx++]);
197 for(i = 0;i < count && idx < count;++i)
199 IMMDevice *device;
201 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
202 continue;
204 if(device != defdev)
205 add_device(device, &devlist[idx++]);
207 IMMDevice_Release(device);
210 if(defdev) IMMDevice_Release(defdev);
211 IMMDeviceCollection_Release(coll);
213 *numdevs = idx;
214 return devlist;
218 static ALuint MMDevApiProc(ALvoid *ptr)
220 ALCdevice *device = ptr;
221 MMDevApiData *data = device->ExtraData;
222 UINT32 buffer_len, written;
223 ALuint update_size, len;
224 BYTE *buffer;
225 HRESULT hr;
227 hr = CoInitialize(NULL);
228 if(FAILED(hr))
230 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
231 aluHandleDisconnect(device);
232 return 0;
235 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
236 if(FAILED(hr))
238 ERR("Failed to get audio buffer size: 0x%08lx\n", hr);
239 aluHandleDisconnect(device);
240 CoUninitialize();
241 return 0;
244 SetRTPriority();
246 update_size = device->UpdateSize;
247 while(!data->killNow)
249 hr = IAudioClient_GetCurrentPadding(data->client, &written);
250 if(FAILED(hr))
252 ERR("Failed to get padding: 0x%08lx\n", hr);
253 aluHandleDisconnect(device);
254 break;
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 aluMixData(device, buffer, len);
272 hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
274 if(FAILED(hr))
276 ERR("Failed to buffer data: 0x%08lx\n", hr);
277 aluHandleDisconnect(device);
278 break;
282 CoUninitialize();
283 return 0;
287 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
289 memset(out, 0, sizeof(*out));
290 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
291 *out = *(const WAVEFORMATEXTENSIBLE*)in;
292 else if(in->wFormatTag == WAVE_FORMAT_PCM)
294 out->Format = *in;
295 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
296 out->Format.cbSize = sizeof(*out) - sizeof(*in);
297 if(out->Format.nChannels == 1)
298 out->dwChannelMask = MONO;
299 else if(out->Format.nChannels == 2)
300 out->dwChannelMask = STEREO;
301 else
302 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
303 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
305 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
307 out->Format = *in;
308 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
309 out->Format.cbSize = sizeof(*out) - sizeof(*in);
310 if(out->Format.nChannels == 1)
311 out->dwChannelMask = MONO;
312 else if(out->Format.nChannels == 2)
313 out->dwChannelMask = STEREO;
314 else
315 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
316 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
318 else
320 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
321 return ALC_FALSE;
323 return ALC_TRUE;
326 static HRESULT DoReset(ALCdevice *device)
328 MMDevApiData *data = device->ExtraData;
329 WAVEFORMATEXTENSIBLE OutputType;
330 WAVEFORMATEX *wfx = NULL;
331 REFERENCE_TIME min_per, buf_time;
332 UINT32 buffer_len, min_len;
333 HRESULT hr;
335 hr = IAudioClient_GetMixFormat(data->client, &wfx);
336 if(FAILED(hr))
338 ERR("Failed to get mix format: 0x%08lx\n", hr);
339 return hr;
342 if(!MakeExtensible(&OutputType, wfx))
344 CoTaskMemFree(wfx);
345 return E_FAIL;
347 CoTaskMemFree(wfx);
348 wfx = NULL;
350 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
351 device->Frequency-1) / device->Frequency;
353 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
354 device->Frequency = OutputType.Format.nSamplesPerSec;
355 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
357 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
358 device->FmtChans = DevFmtMono;
359 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
360 device->FmtChans = DevFmtStereo;
361 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
362 device->FmtChans = DevFmtQuad;
363 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
364 device->FmtChans = DevFmtX51;
365 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
366 device->FmtChans = DevFmtX51Side;
367 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
368 device->FmtChans = DevFmtX61;
369 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
370 device->FmtChans = DevFmtX71;
371 else
372 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
375 switch(device->FmtChans)
377 case DevFmtMono:
378 OutputType.Format.nChannels = 1;
379 OutputType.dwChannelMask = MONO;
380 break;
381 case DevFmtStereo:
382 OutputType.Format.nChannels = 2;
383 OutputType.dwChannelMask = STEREO;
384 break;
385 case DevFmtQuad:
386 OutputType.Format.nChannels = 4;
387 OutputType.dwChannelMask = QUAD;
388 break;
389 case DevFmtX51:
390 OutputType.Format.nChannels = 6;
391 OutputType.dwChannelMask = X5DOT1;
392 break;
393 case DevFmtX51Side:
394 OutputType.Format.nChannels = 6;
395 OutputType.dwChannelMask = X5DOT1SIDE;
396 break;
397 case DevFmtX61:
398 OutputType.Format.nChannels = 7;
399 OutputType.dwChannelMask = X6DOT1;
400 break;
401 case DevFmtX71:
402 OutputType.Format.nChannels = 8;
403 OutputType.dwChannelMask = X7DOT1;
404 break;
406 switch(device->FmtType)
408 case DevFmtByte:
409 device->FmtType = DevFmtUByte;
410 /* fall-through */
411 case DevFmtUByte:
412 OutputType.Format.wBitsPerSample = 8;
413 OutputType.Samples.wValidBitsPerSample = 8;
414 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
415 break;
416 case DevFmtUShort:
417 device->FmtType = DevFmtShort;
418 /* fall-through */
419 case DevFmtShort:
420 OutputType.Format.wBitsPerSample = 16;
421 OutputType.Samples.wValidBitsPerSample = 16;
422 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
423 break;
424 case DevFmtUInt:
425 device->FmtType = DevFmtInt;
426 /* fall-through */
427 case DevFmtInt:
428 OutputType.Format.wBitsPerSample = 32;
429 OutputType.Samples.wValidBitsPerSample = 32;
430 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
431 break;
432 case DevFmtFloat:
433 OutputType.Format.wBitsPerSample = 32;
434 OutputType.Samples.wValidBitsPerSample = 32;
435 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
436 break;
438 OutputType.Format.nSamplesPerSec = device->Frequency;
440 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
441 OutputType.Format.wBitsPerSample / 8;
442 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
443 OutputType.Format.nBlockAlign;
445 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
446 if(FAILED(hr))
448 ERR("Failed to check format support: 0x%08lx\n", hr);
449 hr = IAudioClient_GetMixFormat(data->client, &wfx);
451 if(FAILED(hr))
453 ERR("Failed to find a supported format: 0x%08lx\n", hr);
454 return hr;
457 if(wfx != NULL)
459 if(!MakeExtensible(&OutputType, wfx))
461 CoTaskMemFree(wfx);
462 return E_FAIL;
464 CoTaskMemFree(wfx);
465 wfx = NULL;
467 device->Frequency = OutputType.Format.nSamplesPerSec;
468 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
469 device->FmtChans = DevFmtMono;
470 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
471 device->FmtChans = DevFmtStereo;
472 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
473 device->FmtChans = DevFmtQuad;
474 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
475 device->FmtChans = DevFmtX51;
476 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
477 device->FmtChans = DevFmtX51Side;
478 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
479 device->FmtChans = DevFmtX61;
480 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
481 device->FmtChans = DevFmtX71;
482 else
484 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
485 device->FmtChans = DevFmtStereo;
486 OutputType.Format.nChannels = 2;
487 OutputType.dwChannelMask = STEREO;
490 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
492 if(OutputType.Format.wBitsPerSample == 8)
493 device->FmtType = DevFmtUByte;
494 else if(OutputType.Format.wBitsPerSample == 16)
495 device->FmtType = DevFmtShort;
496 else if(OutputType.Format.wBitsPerSample == 32)
497 device->FmtType = DevFmtInt;
498 else
500 device->FmtType = DevFmtShort;
501 OutputType.Format.wBitsPerSample = 16;
504 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
506 device->FmtType = DevFmtFloat;
507 OutputType.Format.wBitsPerSample = 32;
509 else
511 ERR("Unhandled format sub-type\n");
512 device->FmtType = DevFmtShort;
513 OutputType.Format.wBitsPerSample = 16;
514 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
516 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
519 SetDefaultWFXChannelOrder(device);
521 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
522 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
523 buf_time, 0, &OutputType.Format, NULL);
524 if(FAILED(hr))
526 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
527 return hr;
530 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
531 if(SUCCEEDED(hr))
533 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
534 /* Find the nearest multiple of the period size to the update size */
535 if(min_len < device->UpdateSize)
536 min_len *= (device->UpdateSize + min_len/2)/min_len;
537 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
539 if(FAILED(hr))
541 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
542 return hr;
545 device->UpdateSize = min_len;
546 device->NumUpdates = buffer_len / device->UpdateSize;
547 if(device->NumUpdates <= 1)
549 ERR("Audio client returned buffer_len < period*2; expect break up\n");
550 device->NumUpdates = 2;
551 device->UpdateSize = buffer_len / device->NumUpdates;
554 return hr;
558 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
560 ThreadRequest *req = ptr;
561 IMMDeviceEnumerator *Enumerator;
562 ALuint deviceCount = 0;
563 MMDevApiData *data;
564 ALCdevice *device;
565 HRESULT hr, cohr;
566 MSG msg;
568 TRACE("Starting message thread\n");
570 cohr = CoInitialize(NULL);
571 if(FAILED(cohr))
573 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
574 req->result = cohr;
575 SetEvent(req->FinishedEvt);
576 return 0;
579 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
580 if(FAILED(hr))
582 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
583 CoUninitialize();
584 req->result = hr;
585 SetEvent(req->FinishedEvt);
586 return 0;
588 Enumerator = ptr;
589 IMMDeviceEnumerator_Release(Enumerator);
590 Enumerator = NULL;
592 CoUninitialize();
594 req->result = S_OK;
595 SetEvent(req->FinishedEvt);
597 TRACE("Starting message loop\n");
598 while(GetMessage(&msg, NULL, 0, 0))
600 TRACE("Got message %u\n", msg.message);
601 switch(msg.message)
603 case WM_USER_OpenDevice:
604 req = (ThreadRequest*)msg.wParam;
605 device = (ALCdevice*)msg.lParam;
606 data = device->ExtraData;
608 hr = cohr = S_OK;
609 if(++deviceCount == 1)
610 hr = cohr = CoInitialize(NULL);
611 if(SUCCEEDED(hr))
612 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
613 if(SUCCEEDED(hr))
615 Enumerator = ptr;
616 if(!data->devid)
617 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
618 else
619 hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
620 IMMDeviceEnumerator_Release(Enumerator);
621 Enumerator = NULL;
623 if(SUCCEEDED(hr))
624 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
625 if(SUCCEEDED(hr))
627 data->client = ptr;
628 device->DeviceName = get_device_name(data->mmdev);
631 if(FAILED(hr))
633 if(data->mmdev)
634 IMMDevice_Release(data->mmdev);
635 data->mmdev = NULL;
636 if(--deviceCount == 0 && SUCCEEDED(cohr))
637 CoUninitialize();
640 req->result = hr;
641 SetEvent(req->FinishedEvt);
642 continue;
644 case WM_USER_ResetDevice:
645 req = (ThreadRequest*)msg.wParam;
646 device = (ALCdevice*)msg.lParam;
648 req->result = DoReset(device);
649 SetEvent(req->FinishedEvt);
650 continue;
652 case WM_USER_StartDevice:
653 req = (ThreadRequest*)msg.wParam;
654 device = (ALCdevice*)msg.lParam;
655 data = device->ExtraData;
657 ResetEvent(data->NotifyEvent);
658 hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent);
659 if(FAILED(hr))
660 ERR("Failed to set event handle: 0x%08lx\n", hr);
661 else
663 hr = IAudioClient_Start(data->client);
664 if(FAILED(hr))
665 ERR("Failed to start audio client: 0x%08lx\n", hr);
668 if(SUCCEEDED(hr))
669 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
670 if(SUCCEEDED(hr))
672 data->render = ptr;
673 data->thread = StartThread(MMDevApiProc, device);
674 if(!data->thread)
676 if(data->render)
677 IAudioRenderClient_Release(data->render);
678 data->render = NULL;
679 IAudioClient_Stop(data->client);
680 ERR("Failed to start thread\n");
681 hr = E_FAIL;
685 req->result = hr;
686 SetEvent(req->FinishedEvt);
687 continue;
689 case WM_USER_StopDevice:
690 req = (ThreadRequest*)msg.wParam;
691 device = (ALCdevice*)msg.lParam;
692 data = device->ExtraData;
694 if(data->thread)
696 data->killNow = 1;
697 StopThread(data->thread);
698 data->thread = NULL;
700 data->killNow = 0;
702 IAudioRenderClient_Release(data->render);
703 data->render = NULL;
704 IAudioClient_Stop(data->client);
707 req->result = S_OK;
708 SetEvent(req->FinishedEvt);
709 continue;
711 case WM_USER_CloseDevice:
712 req = (ThreadRequest*)msg.wParam;
713 device = (ALCdevice*)msg.lParam;
714 data = device->ExtraData;
716 IAudioClient_Release(data->client);
717 data->client = NULL;
719 IMMDevice_Release(data->mmdev);
720 data->mmdev = NULL;
722 if(--deviceCount == 0)
723 CoUninitialize();
725 req->result = S_OK;
726 SetEvent(req->FinishedEvt);
727 continue;
729 case WM_USER_Enumerate:
730 req = (ThreadRequest*)msg.wParam;
732 hr = cohr = S_OK;
733 if(++deviceCount == 1)
734 hr = cohr = CoInitialize(NULL);
735 if(SUCCEEDED(hr))
736 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
737 if(SUCCEEDED(hr))
739 EDataFlow flowdir;
740 DevMap **devlist;
741 ALuint *numdevs;
742 ALuint i;
744 Enumerator = ptr;
745 if(msg.lParam == CAPTURE_DEVICE_PROBE)
747 flowdir = eCapture;
748 devlist = &CaptureDeviceList;
749 numdevs = &NumCaptureDevices;
751 else
753 flowdir = eRender;
754 devlist = &PlaybackDeviceList;
755 numdevs = &NumPlaybackDevices;
758 for(i = 0;i < *numdevs;i++)
760 free((*devlist)[i].name);
761 free((*devlist)[i].devid);
763 free(*devlist);
764 *devlist = NULL;
765 *numdevs = 0;
767 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
769 IMMDeviceEnumerator_Release(Enumerator);
770 Enumerator = NULL;
773 if(--deviceCount == 0 && SUCCEEDED(cohr))
774 CoUninitialize();
776 req->result = S_OK;
777 SetEvent(req->FinishedEvt);
778 continue;
780 default:
781 ERR("Unexpected message: %u\n", msg.message);
782 continue;
785 TRACE("Message loop finished\n");
787 return 0;
791 static BOOL MMDevApiLoad(void)
793 static HRESULT InitResult;
794 if(!ThreadHdl)
796 ThreadRequest req;
797 InitResult = E_FAIL;
799 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
800 if(req.FinishedEvt == NULL)
801 ERR("Failed to create event: %lu\n", GetLastError());
802 else
804 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
805 if(ThreadHdl != NULL)
806 InitResult = WaitForResponse(&req);
807 CloseHandle(req.FinishedEvt);
810 return SUCCEEDED(InitResult);
814 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
816 MMDevApiData *data = NULL;
817 HRESULT hr;
819 //Initialise requested device
820 data = calloc(1, sizeof(MMDevApiData));
821 if(!data)
822 return ALC_OUT_OF_MEMORY;
823 device->ExtraData = data;
825 hr = S_OK;
826 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
827 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
828 if(data->NotifyEvent == NULL || data->MsgEvent == NULL)
829 hr = E_FAIL;
831 if(SUCCEEDED(hr))
833 if(deviceName)
835 ALuint i;
837 if(!PlaybackDeviceList)
839 ThreadRequest req = { data->MsgEvent, 0 };
840 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
841 (void)WaitForResponse(&req);
844 hr = E_FAIL;
845 for(i = 0;i < NumPlaybackDevices;i++)
847 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
849 data->devid = strdupW(PlaybackDeviceList[i].devid);
850 hr = S_OK;
851 break;
857 if(SUCCEEDED(hr))
859 ThreadRequest req = { data->MsgEvent, 0 };
861 hr = E_FAIL;
862 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
863 hr = WaitForResponse(&req);
866 if(FAILED(hr))
868 if(data->NotifyEvent != NULL)
869 CloseHandle(data->NotifyEvent);
870 data->NotifyEvent = NULL;
871 if(data->MsgEvent != NULL)
872 CloseHandle(data->MsgEvent);
873 data->MsgEvent = NULL;
875 free(data);
876 device->ExtraData = NULL;
878 ERR("Device init failed: 0x%08lx\n", hr);
879 return ALC_INVALID_VALUE;
882 return ALC_NO_ERROR;
885 static void MMDevApiClosePlayback(ALCdevice *device)
887 MMDevApiData *data = device->ExtraData;
888 ThreadRequest req = { data->MsgEvent, 0 };
890 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
891 (void)WaitForResponse(&req);
893 CloseHandle(data->MsgEvent);
894 data->MsgEvent = NULL;
896 CloseHandle(data->NotifyEvent);
897 data->NotifyEvent = NULL;
899 free(data->devid);
900 data->devid = NULL;
902 free(data);
903 device->ExtraData = NULL;
906 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
908 MMDevApiData *data = device->ExtraData;
909 ThreadRequest req = { data->MsgEvent, 0 };
910 HRESULT hr = E_FAIL;
912 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
913 hr = WaitForResponse(&req);
915 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
918 static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
920 MMDevApiData *data = device->ExtraData;
921 ThreadRequest req = { data->MsgEvent, 0 };
922 HRESULT hr = E_FAIL;
924 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
925 hr = WaitForResponse(&req);
927 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
930 static void MMDevApiStopPlayback(ALCdevice *device)
932 MMDevApiData *data = device->ExtraData;
933 ThreadRequest req = { data->MsgEvent, 0 };
935 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
936 (void)WaitForResponse(&req);
940 static const BackendFuncs MMDevApiFuncs = {
941 MMDevApiOpenPlayback,
942 MMDevApiClosePlayback,
943 MMDevApiResetPlayback,
944 MMDevApiStartPlayback,
945 MMDevApiStopPlayback,
946 NULL,
947 NULL,
948 NULL,
949 NULL,
950 NULL,
951 NULL
955 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
957 if(!MMDevApiLoad())
958 return ALC_FALSE;
959 *FuncList = MMDevApiFuncs;
960 return ALC_TRUE;
963 void alcMMDevApiDeinit(void)
965 ALuint i;
967 for(i = 0;i < NumPlaybackDevices;i++)
969 free(PlaybackDeviceList[i].name);
970 free(PlaybackDeviceList[i].devid);
972 free(PlaybackDeviceList);
973 PlaybackDeviceList = NULL;
974 NumPlaybackDevices = 0;
976 for(i = 0;i < NumCaptureDevices;i++)
978 free(CaptureDeviceList[i].name);
979 free(CaptureDeviceList[i].devid);
981 free(CaptureDeviceList);
982 CaptureDeviceList = NULL;
983 NumCaptureDevices = 0;
985 if(ThreadHdl)
987 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
988 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
989 CloseHandle(ThreadHdl);
990 ThreadHdl = NULL;
994 void alcMMDevApiProbe(enum DevProbe type)
996 ThreadRequest req = { NULL, 0 };
997 HRESULT hr = E_FAIL;
999 switch(type)
1001 case ALL_DEVICE_PROBE:
1002 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
1003 if(req.FinishedEvt == NULL)
1004 ERR("Failed to create event: %lu\n", GetLastError());
1005 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
1006 hr = WaitForResponse(&req);
1007 if(SUCCEEDED(hr))
1009 ALuint i;
1010 for(i = 0;i < NumPlaybackDevices;i++)
1012 if(PlaybackDeviceList[i].name)
1013 AppendAllDevicesList(PlaybackDeviceList[i].name);
1016 break;
1018 case CAPTURE_DEVICE_PROBE:
1019 break;
1021 if(req.FinishedEvt != NULL)
1022 CloseHandle(req.FinishedEvt);
1023 req.FinishedEvt = NULL;