Use the stored buffer metrics for the mmdevapi buffer size
[openal-soft.git] / Alc / backends / mmdevapi.c
blobde4201ddb62bcf9a30814773f8cc1004881ad977
1 /**
2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
21 #include "config.h"
23 #define COBJMACROS
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <memory.h>
28 #include <mmdeviceapi.h>
29 #include <audioclient.h>
30 #include <cguid.h>
31 #include <devpropdef.h>
32 #include <mmreg.h>
33 #include <propsys.h>
34 #include <propkey.h>
35 #include <devpkey.h>
36 #ifndef _WAVEFORMATEXTENSIBLE_
37 #include <ks.h>
38 #include <ksmedia.h>
39 #endif
41 #include "alMain.h"
42 #include "alu.h"
45 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
46 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
48 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
50 #define MONO SPEAKER_FRONT_CENTER
51 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
52 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
53 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
54 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
55 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
56 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
59 typedef struct {
60 WCHAR *devid;
62 IMMDevice *mmdev;
63 IAudioClient *client;
64 IAudioRenderClient *render;
65 HANDLE NotifyEvent;
67 HANDLE MsgEvent;
69 volatile UINT32 Padding;
71 volatile int killNow;
72 ALvoid *thread;
73 } MMDevApiData;
76 typedef struct {
77 ALCchar *name;
78 WCHAR *devid;
79 } DevMap;
81 static DevMap *PlaybackDeviceList;
82 static ALuint NumPlaybackDevices;
83 static DevMap *CaptureDeviceList;
84 static ALuint NumCaptureDevices;
87 static HANDLE ThreadHdl;
88 static DWORD ThreadID;
90 typedef struct {
91 HANDLE FinishedEvt;
92 HRESULT result;
93 } ThreadRequest;
95 #define WM_USER_OpenDevice (WM_USER+0)
96 #define WM_USER_ResetDevice (WM_USER+1)
97 #define WM_USER_StartDevice (WM_USER+2)
98 #define WM_USER_StopDevice (WM_USER+3)
99 #define WM_USER_CloseDevice (WM_USER+4)
100 #define WM_USER_Enumerate (WM_USER+5)
102 static HRESULT WaitForResponse(ThreadRequest *req)
104 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
105 return req->result;
106 ERR("Message response error: %lu\n", GetLastError());
107 return E_FAIL;
111 static ALCchar *get_device_name(IMMDevice *device)
113 ALCchar *name = NULL;
114 IPropertyStore *ps;
115 PROPVARIANT pvname;
116 HRESULT hr;
117 int len;
119 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
120 if(FAILED(hr))
122 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
123 return calloc(1, 1);
126 PropVariantInit(&pvname);
128 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
129 if(FAILED(hr))
131 WARN("GetValue failed: 0x%08lx\n", hr);
132 name = calloc(1, 1);
134 else
136 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
138 name = calloc(1, len);
139 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, name, len, NULL, NULL);
143 PropVariantClear(&pvname);
144 IPropertyStore_Release(ps);
146 return name;
149 static void add_device(IMMDevice *device, DevMap *devmap)
151 LPWSTR devid;
152 HRESULT hr;
154 hr = IMMDevice_GetId(device, &devid);
155 if(SUCCEEDED(hr))
157 devmap->devid = strdupW(devid);
158 devmap->name = get_device_name(device);
159 TRACE("Got device \"%s\", \"%ls\"\n", devmap->name, devmap->devid);
160 CoTaskMemFree(devid);
164 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
166 IMMDeviceCollection *coll;
167 IMMDevice *defdev = NULL;
168 DevMap *devlist = NULL;
169 HRESULT hr;
170 UINT count;
171 UINT idx;
172 UINT i;
174 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
175 if(FAILED(hr))
177 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
178 return NULL;
181 idx = count = 0;
182 hr = IMMDeviceCollection_GetCount(coll, &count);
183 if(SUCCEEDED(hr) && count > 0)
185 devlist = calloc(count, sizeof(*devlist));
186 if(!devlist)
188 IMMDeviceCollection_Release(coll);
189 return NULL;
192 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
193 eMultimedia, &defdev);
195 if(SUCCEEDED(hr) && defdev != NULL)
196 add_device(defdev, &devlist[idx++]);
198 for(i = 0;i < count && idx < count;++i)
200 IMMDevice *device;
202 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
203 continue;
205 if(device != defdev)
206 add_device(device, &devlist[idx++]);
208 IMMDevice_Release(device);
211 if(defdev) IMMDevice_Release(defdev);
212 IMMDeviceCollection_Release(coll);
214 *numdevs = idx;
215 return devlist;
219 static ALuint MMDevApiProc(ALvoid *ptr)
221 ALCdevice *device = ptr;
222 MMDevApiData *data = device->ExtraData;
223 UINT32 buffer_len, written;
224 ALuint update_size, len;
225 BYTE *buffer;
226 HRESULT hr;
228 hr = CoInitialize(NULL);
229 if(FAILED(hr))
231 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
232 aluHandleDisconnect(device);
233 return 0;
236 SetRTPriority();
238 update_size = device->UpdateSize;
239 buffer_len = update_size * device->NumUpdates;
240 while(!data->killNow)
242 hr = IAudioClient_GetCurrentPadding(data->client, &written);
243 if(FAILED(hr))
245 ERR("Failed to get padding: 0x%08lx\n", hr);
246 aluHandleDisconnect(device);
247 break;
249 data->Padding = written;
251 len = buffer_len - written;
252 if(len < update_size)
254 DWORD res;
255 res = WaitForSingleObjectEx(data->NotifyEvent, 2000, FALSE);
256 if(res != WAIT_OBJECT_0)
257 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
258 continue;
260 len -= len%update_size;
262 hr = IAudioRenderClient_GetBuffer(data->render, len, &buffer);
263 if(SUCCEEDED(hr))
265 ALCdevice_Lock(device);
266 aluMixData(device, buffer, len);
267 data->Padding = written + len;
268 ALCdevice_Unlock(device);
269 hr = IAudioRenderClient_ReleaseBuffer(data->render, len, 0);
271 if(FAILED(hr))
273 ERR("Failed to buffer data: 0x%08lx\n", hr);
274 aluHandleDisconnect(device);
275 break;
278 data->Padding = 0;
280 CoUninitialize();
281 return 0;
285 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
287 memset(out, 0, sizeof(*out));
288 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
289 *out = *(const WAVEFORMATEXTENSIBLE*)in;
290 else if(in->wFormatTag == WAVE_FORMAT_PCM)
292 out->Format = *in;
293 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
294 out->Format.cbSize = sizeof(*out) - sizeof(*in);
295 if(out->Format.nChannels == 1)
296 out->dwChannelMask = MONO;
297 else if(out->Format.nChannels == 2)
298 out->dwChannelMask = STEREO;
299 else
300 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
301 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
303 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
305 out->Format = *in;
306 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
307 out->Format.cbSize = sizeof(*out) - sizeof(*in);
308 if(out->Format.nChannels == 1)
309 out->dwChannelMask = MONO;
310 else if(out->Format.nChannels == 2)
311 out->dwChannelMask = STEREO;
312 else
313 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
314 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
316 else
318 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
319 return ALC_FALSE;
321 return ALC_TRUE;
324 static HRESULT DoReset(ALCdevice *device)
326 MMDevApiData *data = device->ExtraData;
327 WAVEFORMATEXTENSIBLE OutputType;
328 WAVEFORMATEX *wfx = NULL;
329 REFERENCE_TIME min_per, buf_time;
330 UINT32 buffer_len, min_len;
331 HRESULT hr;
333 hr = IAudioClient_GetMixFormat(data->client, &wfx);
334 if(FAILED(hr))
336 ERR("Failed to get mix format: 0x%08lx\n", hr);
337 return hr;
340 if(!MakeExtensible(&OutputType, wfx))
342 CoTaskMemFree(wfx);
343 return E_FAIL;
345 CoTaskMemFree(wfx);
346 wfx = NULL;
348 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
349 device->Frequency-1) / device->Frequency;
351 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
352 device->Frequency = OutputType.Format.nSamplesPerSec;
353 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
355 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
356 device->FmtChans = DevFmtMono;
357 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
358 device->FmtChans = DevFmtStereo;
359 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
360 device->FmtChans = DevFmtQuad;
361 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
362 device->FmtChans = DevFmtX51;
363 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
364 device->FmtChans = DevFmtX51Side;
365 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
366 device->FmtChans = DevFmtX61;
367 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
368 device->FmtChans = DevFmtX71;
369 else
370 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
373 switch(device->FmtChans)
375 case DevFmtMono:
376 OutputType.Format.nChannels = 1;
377 OutputType.dwChannelMask = MONO;
378 break;
379 case DevFmtStereo:
380 OutputType.Format.nChannels = 2;
381 OutputType.dwChannelMask = STEREO;
382 break;
383 case DevFmtQuad:
384 OutputType.Format.nChannels = 4;
385 OutputType.dwChannelMask = QUAD;
386 break;
387 case DevFmtX51:
388 OutputType.Format.nChannels = 6;
389 OutputType.dwChannelMask = X5DOT1;
390 break;
391 case DevFmtX51Side:
392 OutputType.Format.nChannels = 6;
393 OutputType.dwChannelMask = X5DOT1SIDE;
394 break;
395 case DevFmtX61:
396 OutputType.Format.nChannels = 7;
397 OutputType.dwChannelMask = X6DOT1;
398 break;
399 case DevFmtX71:
400 OutputType.Format.nChannels = 8;
401 OutputType.dwChannelMask = X7DOT1;
402 break;
404 switch(device->FmtType)
406 case DevFmtByte:
407 device->FmtType = DevFmtUByte;
408 /* fall-through */
409 case DevFmtUByte:
410 OutputType.Format.wBitsPerSample = 8;
411 OutputType.Samples.wValidBitsPerSample = 8;
412 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
413 break;
414 case DevFmtUShort:
415 device->FmtType = DevFmtShort;
416 /* fall-through */
417 case DevFmtShort:
418 OutputType.Format.wBitsPerSample = 16;
419 OutputType.Samples.wValidBitsPerSample = 16;
420 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
421 break;
422 case DevFmtUInt:
423 device->FmtType = DevFmtInt;
424 /* fall-through */
425 case DevFmtInt:
426 OutputType.Format.wBitsPerSample = 32;
427 OutputType.Samples.wValidBitsPerSample = 32;
428 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
429 break;
430 case DevFmtFloat:
431 OutputType.Format.wBitsPerSample = 32;
432 OutputType.Samples.wValidBitsPerSample = 32;
433 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
434 break;
436 OutputType.Format.nSamplesPerSec = device->Frequency;
438 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
439 OutputType.Format.wBitsPerSample / 8;
440 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
441 OutputType.Format.nBlockAlign;
443 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
444 if(FAILED(hr))
446 ERR("Failed to check format support: 0x%08lx\n", hr);
447 hr = IAudioClient_GetMixFormat(data->client, &wfx);
449 if(FAILED(hr))
451 ERR("Failed to find a supported format: 0x%08lx\n", hr);
452 return hr;
455 if(wfx != NULL)
457 if(!MakeExtensible(&OutputType, wfx))
459 CoTaskMemFree(wfx);
460 return E_FAIL;
462 CoTaskMemFree(wfx);
463 wfx = NULL;
465 device->Frequency = OutputType.Format.nSamplesPerSec;
466 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
467 device->FmtChans = DevFmtMono;
468 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
469 device->FmtChans = DevFmtStereo;
470 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
471 device->FmtChans = DevFmtQuad;
472 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
473 device->FmtChans = DevFmtX51;
474 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
475 device->FmtChans = DevFmtX51Side;
476 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
477 device->FmtChans = DevFmtX61;
478 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
479 device->FmtChans = DevFmtX71;
480 else
482 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
483 device->FmtChans = DevFmtStereo;
484 OutputType.Format.nChannels = 2;
485 OutputType.dwChannelMask = STEREO;
488 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
490 if(OutputType.Format.wBitsPerSample == 8)
491 device->FmtType = DevFmtUByte;
492 else if(OutputType.Format.wBitsPerSample == 16)
493 device->FmtType = DevFmtShort;
494 else if(OutputType.Format.wBitsPerSample == 32)
495 device->FmtType = DevFmtInt;
496 else
498 device->FmtType = DevFmtShort;
499 OutputType.Format.wBitsPerSample = 16;
502 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
504 device->FmtType = DevFmtFloat;
505 OutputType.Format.wBitsPerSample = 32;
507 else
509 ERR("Unhandled format sub-type\n");
510 device->FmtType = DevFmtShort;
511 OutputType.Format.wBitsPerSample = 16;
512 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
514 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
517 SetDefaultWFXChannelOrder(device);
519 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
520 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
521 buf_time, 0, &OutputType.Format, NULL);
522 if(FAILED(hr))
524 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
525 return hr;
528 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
529 if(SUCCEEDED(hr))
531 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
532 /* Find the nearest multiple of the period size to the update size */
533 if(min_len < device->UpdateSize)
534 min_len *= (device->UpdateSize + min_len/2)/min_len;
535 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
537 if(FAILED(hr))
539 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
540 return hr;
543 device->UpdateSize = min_len;
544 device->NumUpdates = buffer_len / device->UpdateSize;
545 if(device->NumUpdates <= 1)
547 ERR("Audio client returned buffer_len < period*2; expect break up\n");
548 device->NumUpdates = 2;
549 device->UpdateSize = buffer_len / device->NumUpdates;
552 return hr;
556 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
558 ThreadRequest *req = ptr;
559 IMMDeviceEnumerator *Enumerator;
560 ALuint deviceCount = 0;
561 MMDevApiData *data;
562 ALCdevice *device;
563 HRESULT hr, cohr;
564 MSG msg;
566 TRACE("Starting message thread\n");
568 cohr = CoInitialize(NULL);
569 if(FAILED(cohr))
571 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
572 req->result = cohr;
573 SetEvent(req->FinishedEvt);
574 return 0;
577 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
578 if(FAILED(hr))
580 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
581 CoUninitialize();
582 req->result = hr;
583 SetEvent(req->FinishedEvt);
584 return 0;
586 Enumerator = ptr;
587 IMMDeviceEnumerator_Release(Enumerator);
588 Enumerator = NULL;
590 CoUninitialize();
592 req->result = S_OK;
593 SetEvent(req->FinishedEvt);
595 TRACE("Starting message loop\n");
596 while(GetMessage(&msg, NULL, 0, 0))
598 TRACE("Got message %u\n", msg.message);
599 switch(msg.message)
601 case WM_USER_OpenDevice:
602 req = (ThreadRequest*)msg.wParam;
603 device = (ALCdevice*)msg.lParam;
604 data = device->ExtraData;
606 hr = cohr = S_OK;
607 if(++deviceCount == 1)
608 hr = cohr = CoInitialize(NULL);
609 if(SUCCEEDED(hr))
610 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
611 if(SUCCEEDED(hr))
613 Enumerator = ptr;
614 if(!data->devid)
615 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator, eRender, eMultimedia, &data->mmdev);
616 else
617 hr = IMMDeviceEnumerator_GetDevice(Enumerator, data->devid, &data->mmdev);
618 IMMDeviceEnumerator_Release(Enumerator);
619 Enumerator = NULL;
621 if(SUCCEEDED(hr))
622 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
623 if(SUCCEEDED(hr))
625 data->client = ptr;
626 device->DeviceName = get_device_name(data->mmdev);
629 if(FAILED(hr))
631 if(data->mmdev)
632 IMMDevice_Release(data->mmdev);
633 data->mmdev = NULL;
634 if(--deviceCount == 0 && SUCCEEDED(cohr))
635 CoUninitialize();
638 req->result = hr;
639 SetEvent(req->FinishedEvt);
640 continue;
642 case WM_USER_ResetDevice:
643 req = (ThreadRequest*)msg.wParam;
644 device = (ALCdevice*)msg.lParam;
646 req->result = DoReset(device);
647 SetEvent(req->FinishedEvt);
648 continue;
650 case WM_USER_StartDevice:
651 req = (ThreadRequest*)msg.wParam;
652 device = (ALCdevice*)msg.lParam;
653 data = device->ExtraData;
655 ResetEvent(data->NotifyEvent);
656 hr = IAudioClient_SetEventHandle(data->client, data->NotifyEvent);
657 if(FAILED(hr))
658 ERR("Failed to set event handle: 0x%08lx\n", hr);
659 else
661 hr = IAudioClient_Start(data->client);
662 if(FAILED(hr))
663 ERR("Failed to start audio client: 0x%08lx\n", hr);
666 if(SUCCEEDED(hr))
667 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &ptr);
668 if(SUCCEEDED(hr))
670 data->render = ptr;
671 data->thread = StartThread(MMDevApiProc, device);
672 if(!data->thread)
674 if(data->render)
675 IAudioRenderClient_Release(data->render);
676 data->render = NULL;
677 IAudioClient_Stop(data->client);
678 ERR("Failed to start thread\n");
679 hr = E_FAIL;
683 req->result = hr;
684 SetEvent(req->FinishedEvt);
685 continue;
687 case WM_USER_StopDevice:
688 req = (ThreadRequest*)msg.wParam;
689 device = (ALCdevice*)msg.lParam;
690 data = device->ExtraData;
692 if(data->thread)
694 data->killNow = 1;
695 StopThread(data->thread);
696 data->thread = NULL;
698 data->killNow = 0;
700 IAudioRenderClient_Release(data->render);
701 data->render = NULL;
702 IAudioClient_Stop(data->client);
705 req->result = S_OK;
706 SetEvent(req->FinishedEvt);
707 continue;
709 case WM_USER_CloseDevice:
710 req = (ThreadRequest*)msg.wParam;
711 device = (ALCdevice*)msg.lParam;
712 data = device->ExtraData;
714 IAudioClient_Release(data->client);
715 data->client = NULL;
717 IMMDevice_Release(data->mmdev);
718 data->mmdev = NULL;
720 if(--deviceCount == 0)
721 CoUninitialize();
723 req->result = S_OK;
724 SetEvent(req->FinishedEvt);
725 continue;
727 case WM_USER_Enumerate:
728 req = (ThreadRequest*)msg.wParam;
730 hr = cohr = S_OK;
731 if(++deviceCount == 1)
732 hr = cohr = CoInitialize(NULL);
733 if(SUCCEEDED(hr))
734 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
735 if(SUCCEEDED(hr))
737 EDataFlow flowdir;
738 DevMap **devlist;
739 ALuint *numdevs;
740 ALuint i;
742 Enumerator = ptr;
743 if(msg.lParam == CAPTURE_DEVICE_PROBE)
745 flowdir = eCapture;
746 devlist = &CaptureDeviceList;
747 numdevs = &NumCaptureDevices;
749 else
751 flowdir = eRender;
752 devlist = &PlaybackDeviceList;
753 numdevs = &NumPlaybackDevices;
756 for(i = 0;i < *numdevs;i++)
758 free((*devlist)[i].name);
759 free((*devlist)[i].devid);
761 free(*devlist);
762 *devlist = NULL;
763 *numdevs = 0;
765 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
767 IMMDeviceEnumerator_Release(Enumerator);
768 Enumerator = NULL;
771 if(--deviceCount == 0 && SUCCEEDED(cohr))
772 CoUninitialize();
774 req->result = S_OK;
775 SetEvent(req->FinishedEvt);
776 continue;
778 default:
779 ERR("Unexpected message: %u\n", msg.message);
780 continue;
783 TRACE("Message loop finished\n");
785 return 0;
789 static BOOL MMDevApiLoad(void)
791 static HRESULT InitResult;
792 if(!ThreadHdl)
794 ThreadRequest req;
795 InitResult = E_FAIL;
797 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
798 if(req.FinishedEvt == NULL)
799 ERR("Failed to create event: %lu\n", GetLastError());
800 else
802 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
803 if(ThreadHdl != NULL)
804 InitResult = WaitForResponse(&req);
805 CloseHandle(req.FinishedEvt);
808 return SUCCEEDED(InitResult);
812 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
814 MMDevApiData *data = NULL;
815 HRESULT hr;
817 //Initialise requested device
818 data = calloc(1, sizeof(MMDevApiData));
819 if(!data)
820 return ALC_OUT_OF_MEMORY;
821 device->ExtraData = data;
823 hr = S_OK;
824 data->NotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
825 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
826 if(data->NotifyEvent == NULL || data->MsgEvent == NULL)
827 hr = E_FAIL;
829 if(SUCCEEDED(hr))
831 if(deviceName)
833 ALuint i;
835 if(!PlaybackDeviceList)
837 ThreadRequest req = { data->MsgEvent, 0 };
838 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
839 (void)WaitForResponse(&req);
842 hr = E_FAIL;
843 for(i = 0;i < NumPlaybackDevices;i++)
845 if(strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
847 data->devid = strdupW(PlaybackDeviceList[i].devid);
848 hr = S_OK;
849 break;
855 if(SUCCEEDED(hr))
857 ThreadRequest req = { data->MsgEvent, 0 };
859 hr = E_FAIL;
860 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
861 hr = WaitForResponse(&req);
864 if(FAILED(hr))
866 if(data->NotifyEvent != NULL)
867 CloseHandle(data->NotifyEvent);
868 data->NotifyEvent = NULL;
869 if(data->MsgEvent != NULL)
870 CloseHandle(data->MsgEvent);
871 data->MsgEvent = NULL;
873 free(data);
874 device->ExtraData = NULL;
876 ERR("Device init failed: 0x%08lx\n", hr);
877 return ALC_INVALID_VALUE;
880 return ALC_NO_ERROR;
883 static void MMDevApiClosePlayback(ALCdevice *device)
885 MMDevApiData *data = device->ExtraData;
886 ThreadRequest req = { data->MsgEvent, 0 };
888 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
889 (void)WaitForResponse(&req);
891 CloseHandle(data->MsgEvent);
892 data->MsgEvent = NULL;
894 CloseHandle(data->NotifyEvent);
895 data->NotifyEvent = NULL;
897 free(data->devid);
898 data->devid = NULL;
900 free(data);
901 device->ExtraData = NULL;
904 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
906 MMDevApiData *data = device->ExtraData;
907 ThreadRequest req = { data->MsgEvent, 0 };
908 HRESULT hr = E_FAIL;
910 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
911 hr = WaitForResponse(&req);
913 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
916 static ALCboolean MMDevApiStartPlayback(ALCdevice *device)
918 MMDevApiData *data = device->ExtraData;
919 ThreadRequest req = { data->MsgEvent, 0 };
920 HRESULT hr = E_FAIL;
922 if(PostThreadMessage(ThreadID, WM_USER_StartDevice, (WPARAM)&req, (LPARAM)device))
923 hr = WaitForResponse(&req);
925 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
928 static void MMDevApiStopPlayback(ALCdevice *device)
930 MMDevApiData *data = device->ExtraData;
931 ThreadRequest req = { data->MsgEvent, 0 };
933 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
934 (void)WaitForResponse(&req);
938 static ALint64 MMDevApiGetLatency(ALCdevice *device)
940 MMDevApiData *data = device->ExtraData;
942 return (ALint64)data->Padding * 1000000000 / device->Frequency;
946 static const BackendFuncs MMDevApiFuncs = {
947 MMDevApiOpenPlayback,
948 MMDevApiClosePlayback,
949 MMDevApiResetPlayback,
950 MMDevApiStartPlayback,
951 MMDevApiStopPlayback,
952 NULL,
953 NULL,
954 NULL,
955 NULL,
956 NULL,
957 NULL,
958 ALCdevice_LockDefault,
959 ALCdevice_UnlockDefault,
960 MMDevApiGetLatency
964 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
966 if(!MMDevApiLoad())
967 return ALC_FALSE;
968 *FuncList = MMDevApiFuncs;
969 return ALC_TRUE;
972 void alcMMDevApiDeinit(void)
974 ALuint i;
976 for(i = 0;i < NumPlaybackDevices;i++)
978 free(PlaybackDeviceList[i].name);
979 free(PlaybackDeviceList[i].devid);
981 free(PlaybackDeviceList);
982 PlaybackDeviceList = NULL;
983 NumPlaybackDevices = 0;
985 for(i = 0;i < NumCaptureDevices;i++)
987 free(CaptureDeviceList[i].name);
988 free(CaptureDeviceList[i].devid);
990 free(CaptureDeviceList);
991 CaptureDeviceList = NULL;
992 NumCaptureDevices = 0;
994 if(ThreadHdl)
996 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
997 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
998 CloseHandle(ThreadHdl);
999 ThreadHdl = NULL;
1003 void alcMMDevApiProbe(enum DevProbe type)
1005 ThreadRequest req = { NULL, 0 };
1006 HRESULT hr = E_FAIL;
1008 switch(type)
1010 case ALL_DEVICE_PROBE:
1011 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
1012 if(req.FinishedEvt == NULL)
1013 ERR("Failed to create event: %lu\n", GetLastError());
1014 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
1015 hr = WaitForResponse(&req);
1016 if(SUCCEEDED(hr))
1018 ALuint i;
1019 for(i = 0;i < NumPlaybackDevices;i++)
1021 if(PlaybackDeviceList[i].name)
1022 AppendAllDevicesList(PlaybackDeviceList[i].name);
1025 break;
1027 case CAPTURE_DEVICE_PROBE:
1028 break;
1030 if(req.FinishedEvt != NULL)
1031 CloseHandle(req.FinishedEvt);
1032 req.FinishedEvt = NULL;