Add an example program showing how to apply reverb to a source
[openal-soft.git] / Alc / backends / mmdevapi.c
blob2431416489f3daddb96e803ab6371843cf723cba
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 int killNow;
70 ALvoid *thread;
71 } MMDevApiData;
74 typedef struct {
75 ALCchar *name;
76 WCHAR *devid;
77 } DevMap;
79 static DevMap *PlaybackDeviceList;
80 static ALuint NumPlaybackDevices;
81 static DevMap *CaptureDeviceList;
82 static ALuint NumCaptureDevices;
85 static HANDLE ThreadHdl;
86 static DWORD ThreadID;
88 typedef struct {
89 HANDLE FinishedEvt;
90 HRESULT result;
91 } ThreadRequest;
93 #define WM_USER_OpenDevice (WM_USER+0)
94 #define WM_USER_ResetDevice (WM_USER+1)
95 #define WM_USER_StartDevice (WM_USER+2)
96 #define WM_USER_StopDevice (WM_USER+3)
97 #define WM_USER_CloseDevice (WM_USER+4)
98 #define WM_USER_Enumerate (WM_USER+5)
100 static HRESULT WaitForResponse(ThreadRequest *req)
102 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
103 return req->result;
104 ERR("Message response error: %lu\n", GetLastError());
105 return E_FAIL;
109 static ALCchar *get_device_name(IMMDevice *device)
111 ALCchar *name = NULL;
112 IPropertyStore *ps;
113 PROPVARIANT pvname;
114 HRESULT hr;
115 int len;
117 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
118 if(FAILED(hr))
120 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
121 return calloc(1, 1);
124 PropVariantInit(&pvname);
126 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
127 if(FAILED(hr))
129 WARN("GetValue failed: 0x%08lx\n", hr);
130 name = calloc(1, 1);
132 else
134 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
136 name = calloc(1, len);
137 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL);
141 PropVariantClear(&pvname);
142 IPropertyStore_Release(ps);
144 return name;
147 static void add_device(IMMDevice *device, DevMap *devmap)
149 LPWSTR devid;
150 HRESULT hr;
152 hr = IMMDevice_GetId(device, &devid);
153 if(SUCCEEDED(hr))
155 devmap->devid = strdupW(devid);
156 devmap->name = get_device_name(device);
157 TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid);
158 CoTaskMemFree(devid);
162 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
164 IMMDeviceCollection *coll;
165 IMMDevice *defdev = NULL;
166 DevMap *devlist = NULL;
167 HRESULT hr;
168 UINT count;
169 UINT idx;
170 UINT i;
172 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
173 if(FAILED(hr))
175 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
176 return NULL;
179 idx = count = 0;
180 hr = IMMDeviceCollection_GetCount(coll, &count);
181 if(SUCCEEDED(hr) && count > 0)
183 devlist = calloc(count, sizeof(*devlist));
184 if(!devlist)
186 IMMDeviceCollection_Release(coll);
187 return NULL;
190 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
191 eMultimedia, &defdev);
193 if(SUCCEEDED(hr) && defdev != NULL)
194 add_device(defdev, &devlist[idx++]);
196 for(i = 0;i < count && idx < count;++i)
198 IMMDevice *device;
200 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
201 continue;
203 if(device != defdev)
204 add_device(device, &devlist[idx++]);
206 IMMDevice_Release(device);
209 if(defdev) IMMDevice_Release(defdev);
210 IMMDeviceCollection_Release(coll);
212 *numdevs = idx;
213 return devlist;
217 static ALuint MMDevApiProc(ALvoid *ptr)
219 ALCdevice *device = ptr;
220 MMDevApiData *data = device->ExtraData;
221 UINT32 buffer_len, written;
222 ALuint update_size, len;
223 BYTE *buffer;
224 HRESULT hr;
226 hr = CoInitialize(NULL);
227 if(FAILED(hr))
229 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
230 aluHandleDisconnect(device);
231 return 0;
234 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
235 if(FAILED(hr))
237 ERR("Failed to get audio buffer size: 0x%08lx\n", hr);
238 aluHandleDisconnect(device);
239 CoUninitialize();
240 return 0;
243 SetRTPriority();
245 update_size = device->UpdateSize;
246 while(!data->killNow)
248 hr = IAudioClient_GetCurrentPadding(data->client, &written);
249 if(FAILED(hr))
251 ERR("Failed to get padding: 0x%08lx\n", hr);
252 aluHandleDisconnect(device);
253 break;
256 len = buffer_len - written;
257 if(len < update_size)
259 DWORD res;
260 res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
261 if(res != WAIT_OBJECT_0)
262 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
263 continue;
265 len -= len%update_size;
267 hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer);
268 if(SUCCEEDED(hr))
270 aluMixData(device, buffer, len);
271 hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
273 if(FAILED(hr))
275 ERR("Failed to buffer data: 0x%08lx\n", hr);
276 aluHandleDisconnect(device);
277 break;
281 CoUninitialize();
282 return 0;
286 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
288 memset(out, 0, sizeof(*out));
289 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
290 *out = *(const WAVEFORMATEXTENSIBLE*)in;
291 else if(in->wFormatTag == WAVE_FORMAT_PCM)
293 out->Format = *in;
294 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
295 out->Format.cbSize = sizeof(*out) - sizeof(*in);
296 if(out->Format.nChannels == 1)
297 out->dwChannelMask = MONO;
298 else if(out->Format.nChannels == 2)
299 out->dwChannelMask = STEREO;
300 else
301 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
302 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
304 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
306 out->Format = *in;
307 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
308 out->Format.cbSize = sizeof(*out) - sizeof(*in);
309 if(out->Format.nChannels == 1)
310 out->dwChannelMask = MONO;
311 else if(out->Format.nChannels == 2)
312 out->dwChannelMask = STEREO;
313 else
314 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
315 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
317 else
319 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
320 return ALC_FALSE;
322 return ALC_TRUE;
325 static HRESULT DoReset(ALCdevice *device)
327 MMDevApiData *data = device->ExtraData;
328 WAVEFORMATEXTENSIBLE OutputType;
329 WAVEFORMATEX *wfx = NULL;
330 REFERENCE_TIME min_per, buf_time;
331 UINT32 buffer_len, min_len;
332 HRESULT hr;
334 hr = IAudioClient_GetMixFormat(data->client, &wfx);
335 if(FAILED(hr))
337 ERR("Failed to get mix format: 0x%08lx\n", hr);
338 return hr;
341 if(!MakeExtensible(&OutputType, wfx))
343 CoTaskMemFree(wfx);
344 return E_FAIL;
346 CoTaskMemFree(wfx);
347 wfx = NULL;
349 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
350 device->Frequency-1) / device->Frequency;
352 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
353 device->Frequency = OutputType.Format.nSamplesPerSec;
354 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
356 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
357 device->FmtChans = DevFmtMono;
358 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
359 device->FmtChans = DevFmtStereo;
360 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
361 device->FmtChans = DevFmtQuad;
362 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
363 device->FmtChans = DevFmtX51;
364 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
365 device->FmtChans = DevFmtX51Side;
366 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
367 device->FmtChans = DevFmtX61;
368 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
369 device->FmtChans = DevFmtX71;
370 else
371 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
374 switch(device->FmtChans)
376 case DevFmtMono:
377 OutputType.Format.nChannels = 1;
378 OutputType.dwChannelMask = MONO;
379 break;
380 case DevFmtStereo:
381 OutputType.Format.nChannels = 2;
382 OutputType.dwChannelMask = STEREO;
383 break;
384 case DevFmtQuad:
385 OutputType.Format.nChannels = 4;
386 OutputType.dwChannelMask = QUAD;
387 break;
388 case DevFmtX51:
389 OutputType.Format.nChannels = 6;
390 OutputType.dwChannelMask = X5DOT1;
391 break;
392 case DevFmtX51Side:
393 OutputType.Format.nChannels = 6;
394 OutputType.dwChannelMask = X5DOT1SIDE;
395 break;
396 case DevFmtX61:
397 OutputType.Format.nChannels = 7;
398 OutputType.dwChannelMask = X6DOT1;
399 break;
400 case DevFmtX71:
401 OutputType.Format.nChannels = 8;
402 OutputType.dwChannelMask = X7DOT1;
403 break;
405 switch(device->FmtType)
407 case DevFmtByte:
408 device->FmtType = DevFmtUByte;
409 /* fall-through */
410 case DevFmtUByte:
411 OutputType.Format.wBitsPerSample = 8;
412 OutputType.Samples.wValidBitsPerSample = 8;
413 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
414 break;
415 case DevFmtUShort:
416 device->FmtType = DevFmtShort;
417 /* fall-through */
418 case DevFmtShort:
419 OutputType.Format.wBitsPerSample = 16;
420 OutputType.Samples.wValidBitsPerSample = 16;
421 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
422 break;
423 case DevFmtUInt:
424 device->FmtType = DevFmtInt;
425 /* fall-through */
426 case DevFmtInt:
427 OutputType.Format.wBitsPerSample = 32;
428 OutputType.Samples.wValidBitsPerSample = 32;
429 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
430 break;
431 case DevFmtFloat:
432 OutputType.Format.wBitsPerSample = 32;
433 OutputType.Samples.wValidBitsPerSample = 32;
434 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
435 break;
437 OutputType.Format.nSamplesPerSec = device->Frequency;
439 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
440 OutputType.Format.wBitsPerSample / 8;
441 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
442 OutputType.Format.nBlockAlign;
444 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
445 if(FAILED(hr))
447 ERR("Failed to check format support: 0x%08lx\n", hr);
448 hr = IAudioClient_GetMixFormat(data->client, &wfx);
450 if(FAILED(hr))
452 ERR("Failed to find a supported format: 0x%08lx\n", hr);
453 return hr;
456 if(wfx != NULL)
458 if(!MakeExtensible(&OutputType, wfx))
460 CoTaskMemFree(wfx);
461 return E_FAIL;
463 CoTaskMemFree(wfx);
464 wfx = NULL;
466 device->Frequency = OutputType.Format.nSamplesPerSec;
467 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
468 device->FmtChans = DevFmtMono;
469 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
470 device->FmtChans = DevFmtStereo;
471 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
472 device->FmtChans = DevFmtQuad;
473 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
474 device->FmtChans = DevFmtX51;
475 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
476 device->FmtChans = DevFmtX51Side;
477 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
478 device->FmtChans = DevFmtX61;
479 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
480 device->FmtChans = DevFmtX71;
481 else
483 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
484 device->FmtChans = DevFmtStereo;
485 OutputType.Format.nChannels = 2;
486 OutputType.dwChannelMask = STEREO;
489 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
491 if(OutputType.Format.wBitsPerSample == 8)
492 device->FmtType = DevFmtUByte;
493 else if(OutputType.Format.wBitsPerSample == 16)
494 device->FmtType = DevFmtShort;
495 else if(OutputType.Format.wBitsPerSample == 32)
496 device->FmtType = DevFmtInt;
497 else
499 device->FmtType = DevFmtShort;
500 OutputType.Format.wBitsPerSample = 16;
503 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
505 device->FmtType = DevFmtFloat;
506 OutputType.Format.wBitsPerSample = 32;
508 else
510 ERR("Unhandled format sub-type\n");
511 device->FmtType = DevFmtShort;
512 OutputType.Format.wBitsPerSample = 16;
513 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
515 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
518 SetDefaultWFXChannelOrder(device);
520 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
521 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
522 buf_time, 0, &OutputType.Format, NULL);
523 if(FAILED(hr))
525 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
526 return hr;
529 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
530 if(SUCCEEDED(hr))
532 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
533 /* Find the nearest multiple of the period size to the update size */
534 if(min_len < device->UpdateSize)
535 min_len *= (device->UpdateSize + min_len/2)/min_len;
536 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
538 if(FAILED(hr))
540 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
541 return hr;
544 device->UpdateSize = min_len;
545 device->NumUpdates = buffer_len / device->UpdateSize;
546 if(device->NumUpdates <= 1)
548 ERR("Audio client returned buffer_len < period*2; expect break up\n");
549 device->NumUpdates = 2;
550 device->UpdateSize = buffer_len / device->NumUpdates;
553 return hr;
557 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
559 ThreadRequest *req = ptr;
560 IMMDeviceEnumerator *Enumerator;
561 ALuint deviceCount = 0;
562 MMDevApiData *data;
563 ALCdevice *device;
564 HRESULT hr, cohr;
565 MSG msg;
567 TRACE("Starting message thread\n");
569 cohr = CoInitialize(NULL);
570 if(FAILED(cohr))
572 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
573 req->result = cohr;
574 SetEvent(req->FinishedEvt);
575 return 0;
578 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
579 if(FAILED(hr))
581 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
582 CoUninitialize();
583 req->result = hr;
584 SetEvent(req->FinishedEvt);
585 return 0;
587 Enumerator = ptr;
588 IMMDeviceEnumerator_Release(Enumerator);
589 Enumerator = NULL;
591 CoUninitialize();
593 req->result = S_OK;
594 SetEvent(req->FinishedEvt);
596 TRACE("Starting message loop\n");
597 while(GetMessage(&msg, NULL, 0, 0))
599 TRACE("Got message %u\n", msg.message);
600 switch(msg.message)
602 case WM_USER_OpenDevice:
603 req = (ThreadRequest*)msg.wParam;
604 device = (ALCdevice*)msg.lParam;
605 data = device->ExtraData;
607 hr = cohr = S_OK;
608 if(++deviceCount == 1)
609 hr = cohr = CoInitialize(NULL);
610 if(SUCCEEDED(hr))
611 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
612 if(SUCCEEDED(hr))
614 Enumerator = ptr;
615 if(!data->devid)
616 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
617 else
618 hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
619 IMMDeviceEnumerator_Release(Enumerator);
620 Enumerator = NULL;
622 if(SUCCEEDED(hr))
623 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
624 if(SUCCEEDED(hr))
626 data->client = ptr;
627 device->DeviceName = get_device_name(data->mmdev);
630 if(FAILED(hr))
632 if(data->mmdev)
633 IMMDevice_Release(data->mmdev);
634 data->mmdev = NULL;
635 if(--deviceCount == 0 && SUCCEEDED(cohr))
636 CoUninitialize();
639 req->result = hr;
640 SetEvent(req->FinishedEvt);
641 continue;
643 case WM_USER_ResetDevice:
644 req = (ThreadRequest*)msg.wParam;
645 device = (ALCdevice*)msg.lParam;
647 req->result = DoReset(device);
648 SetEvent(req->FinishedEvt);
649 continue;
651 case WM_USER_StartDevice:
652 req = (ThreadRequest*)msg.wParam;
653 device = (ALCdevice*)msg.lParam;
654 data = device->ExtraData;
656 ResetEvent(data->NotifyEvent);
657 hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent);
658 if(FAILED(hr))
659 ERR("Failed to set event handle: 0x%08lx\n", hr);
660 else
662 hr = IAudioClient_Start(data->client);
663 if(FAILED(hr))
664 ERR("Failed to start audio client: 0x%08lx\n", hr);
667 if(SUCCEEDED(hr))
668 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
669 if(SUCCEEDED(hr))
671 data->render = ptr;
672 data->thread = StartThread(MMDevApiProc, device);
673 if(!data->thread)
675 if(data->render)
676 IAudioRenderClient_Release(data->render);
677 data->render = NULL;
678 IAudioClient_Stop(data->client);
679 ERR("Failed to start thread\n");
680 hr = E_FAIL;
684 req->result = hr;
685 SetEvent(req->FinishedEvt);
686 continue;
688 case WM_USER_StopDevice:
689 req = (ThreadRequest*)msg.wParam;
690 device = (ALCdevice*)msg.lParam;
691 data = device->ExtraData;
693 if(data->thread)
695 data->killNow = 1;
696 StopThread(data->thread);
697 data->thread = NULL;
699 data->killNow = 0;
701 IAudioRenderClient_Release(data->render);
702 data->render = NULL;
703 IAudioClient_Stop(data->client);
706 req->result = S_OK;
707 SetEvent(req->FinishedEvt);
708 continue;
710 case WM_USER_CloseDevice:
711 req = (ThreadRequest*)msg.wParam;
712 device = (ALCdevice*)msg.lParam;
713 data = device->ExtraData;
715 IAudioClient_Release(data->client);
716 data->client = NULL;
718 IMMDevice_Release(data->mmdev);
719 data->mmdev = NULL;
721 if(--deviceCount == 0)
722 CoUninitialize();
724 req->result = S_OK;
725 SetEvent(req->FinishedEvt);
726 continue;
728 case WM_USER_Enumerate:
729 req = (ThreadRequest*)msg.wParam;
731 hr = cohr = S_OK;
732 if(++deviceCount == 1)
733 hr = cohr = CoInitialize(NULL);
734 if(SUCCEEDED(hr))
735 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
736 if(SUCCEEDED(hr))
738 EDataFlow flowdir;
739 DevMap **devlist;
740 ALuint *numdevs;
741 ALuint i;
743 Enumerator = ptr;
744 if(msg.lParam == CAPTURE_DEVICE_PROBE)
746 flowdir = eCapture;
747 devlist = &CaptureDeviceList;
748 numdevs = &NumCaptureDevices;
750 else
752 flowdir = eRender;
753 devlist = &PlaybackDeviceList;
754 numdevs = &NumPlaybackDevices;
757 for(i = 0;i < *numdevs;i++)
759 free((*devlist)[i].name);
760 free((*devlist)[i].devid);
762 free(*devlist);
763 *devlist = NULL;
764 *numdevs = 0;
766 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
768 IMMDeviceEnumerator_Release(Enumerator);
769 Enumerator = NULL;
772 if(--deviceCount == 0 && SUCCEEDED(cohr))
773 CoUninitialize();
775 req->result = S_OK;
776 SetEvent(req->FinishedEvt);
777 continue;
779 default:
780 ERR("Unexpected message: %u\n", msg.message);
781 continue;
784 TRACE("Message loop finished\n");
786 return 0;
790 static BOOL MMDevApiLoad(void)
792 static HRESULT InitResult;
793 if(!ThreadHdl)
795 ThreadRequest req;
796 InitResult = E_FAIL;
798 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
799 if(req.FinishedEvt == NULL)
800 ERR("Failed to create event: %lu\n", GetLastError());
801 else
803 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
804 if(ThreadHdl != NULL)
805 InitResult = WaitForResponse(&req);
806 CloseHandle(req.FinishedEvt);
809 return SUCCEEDED(InitResult);
813 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
815 MMDevApiData *data = NULL;
816 HRESULT hr;
818 //Initialise requested device
819 data = calloc(1, sizeof(MMDevApiData));
820 if(!data)
821 return ALC_OUT_OF_MEMORY;
822 device->ExtraData = data;
824 hr = S_OK;
825 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
826 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
827 if(data->NotifyEvent == NULL || data->MsgEvent == NULL)
828 hr = E_FAIL;
830 if(SUCCEEDED(hr))
832 if(deviceName)
834 ALuint i;
836 if(!PlaybackDeviceList)
838 ThreadRequest req = { data->MsgEvent, 0 };
839 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
840 (void)WaitForResponse(&req);
843 hr = E_FAIL;
844 for(i = 0;i < NumPlaybackDevices;i++)
846 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
848 data->devid = strdupW(PlaybackDeviceList[i].devid);
849 hr = S_OK;
850 break;
856 if(SUCCEEDED(hr))
858 ThreadRequest req = { data->MsgEvent, 0 };
860 hr = E_FAIL;
861 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
862 hr = WaitForResponse(&req);
865 if(FAILED(hr))
867 if(data->NotifyEvent != NULL)
868 CloseHandle(data->NotifyEvent);
869 data->NotifyEvent = NULL;
870 if(data->MsgEvent != NULL)
871 CloseHandle(data->MsgEvent);
872 data->MsgEvent = NULL;
874 free(data);
875 device->ExtraData = NULL;
877 ERR("Device init failed: 0x%08lx\n", hr);
878 return ALC_INVALID_VALUE;
881 return ALC_NO_ERROR;
884 static void MMDevApiClosePlayback(ALCdevice *device)
886 MMDevApiData *data = device->ExtraData;
887 ThreadRequest req = { data->MsgEvent, 0 };
889 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
890 (void)WaitForResponse(&req);
892 CloseHandle(data->MsgEvent);
893 data->MsgEvent = NULL;
895 CloseHandle(data->NotifyEvent);
896 data->NotifyEvent = NULL;
898 free(data->devid);
899 data->devid = NULL;
901 free(data);
902 device->ExtraData = NULL;
905 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
907 MMDevApiData *data = device->ExtraData;
908 ThreadRequest req = { data->MsgEvent, 0 };
909 HRESULT hr = E_FAIL;
911 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
912 hr = WaitForResponse(&req);
914 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
917 static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
919 MMDevApiData *data = device->ExtraData;
920 ThreadRequest req = { data->MsgEvent, 0 };
921 HRESULT hr = E_FAIL;
923 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
924 hr = WaitForResponse(&req);
926 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
929 static void MMDevApiStopPlayback(ALCdevice *device)
931 MMDevApiData *data = device->ExtraData;
932 ThreadRequest req = { data->MsgEvent, 0 };
934 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
935 (void)WaitForResponse(&req);
939 static const BackendFuncs MMDevApiFuncs = {
940 MMDevApiOpenPlayback,
941 MMDevApiClosePlayback,
942 MMDevApiResetPlayback,
943 MMDevApiStartPlayback,
944 MMDevApiStopPlayback,
945 NULL,
946 NULL,
947 NULL,
948 NULL,
949 NULL,
950 NULL,
951 ALCdevice_LockDefault,
952 ALCdevice_UnlockDefault,
953 ALCdevice_GetLatencyDefault
957 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
959 if(!MMDevApiLoad())
960 return ALC_FALSE;
961 *FuncList = MMDevApiFuncs;
962 return ALC_TRUE;
965 void alcMMDevApiDeinit(void)
967 ALuint i;
969 for(i = 0;i < NumPlaybackDevices;i++)
971 free(PlaybackDeviceList[i].name);
972 free(PlaybackDeviceList[i].devid);
974 free(PlaybackDeviceList);
975 PlaybackDeviceList = NULL;
976 NumPlaybackDevices = 0;
978 for(i = 0;i < NumCaptureDevices;i++)
980 free(CaptureDeviceList[i].name);
981 free(CaptureDeviceList[i].devid);
983 free(CaptureDeviceList);
984 CaptureDeviceList = NULL;
985 NumCaptureDevices = 0;
987 if(ThreadHdl)
989 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
990 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
991 CloseHandle(ThreadHdl);
992 ThreadHdl = NULL;
996 void alcMMDevApiProbe(enum DevProbe type)
998 ThreadRequest req = { NULL, 0 };
999 HRESULT hr = E_FAIL;
1001 switch(type)
1003 case ALL_DEVICE_PROBE:
1004 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
1005 if(req.FinishedEvt == NULL)
1006 ERR("Failed to create event: %lu\n", GetLastError());
1007 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
1008 hr = WaitForResponse(&req);
1009 if(SUCCEEDED(hr))
1011 ALuint i;
1012 for(i = 0;i < NumPlaybackDevices;i++)
1014 if(PlaybackDeviceList[i].name)
1015 AppendAllDevicesList(PlaybackDeviceList[i].name);
1018 break;
1020 case CAPTURE_DEVICE_PROBE:
1021 break;
1023 if(req.FinishedEvt != NULL)
1024 CloseHandle(req.FinishedEvt);
1025 req.FinishedEvt = NULL;