Use local variables to determine how much to write to mmdevapi
[openal-soft/openal-hmr.git] / Alc / backends / mmdevapi.c
blob2b3cfa8b80ca76569f1c310cdd4958af27c8a4c9
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 DevMap *PlaybackDeviceList;
76 static ALuint NumPlaybackDevices;
77 static DevMap *CaptureDeviceList;
78 static ALuint NumCaptureDevices;
81 static HANDLE ThreadHdl;
82 static DWORD ThreadID;
84 typedef struct {
85 HANDLE FinishedEvt;
86 HRESULT result;
87 } ThreadRequest;
89 #define WM_USER_OpenDevice (WM_USER+0)
90 #define WM_USER_ResetDevice (WM_USER+1)
91 #define WM_USER_StopDevice (WM_USER+2)
92 #define WM_USER_CloseDevice (WM_USER+3)
93 #define WM_USER_Enumerate (WM_USER+4)
95 static HRESULT WaitForResponse(ThreadRequest *req)
97 if(WaitForSingleObject(req->FinishedEvt, INFINITE) == WAIT_OBJECT_0)
98 return req->result;
99 ERR("Message response error: %lu\n", GetLastError());
100 return E_FAIL;
104 static HRESULT get_mmdevice_by_guid(IMMDeviceEnumerator *devenum, EDataFlow flowdir, const GUID *guid, IMMDevice **out)
106 IMMDeviceCollection *coll;
107 UINT count, i;
108 HRESULT hr;
110 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
111 if(FAILED(hr))
113 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
114 return hr;
117 count = 0;
118 IMMDeviceCollection_GetCount(coll, &count);
119 for(i = 0;i < count;++i)
121 IMMDevice *device;
122 IPropertyStore *ps;
123 PROPVARIANT pv;
124 GUID devid;
126 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
127 continue;
129 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
130 if(FAILED(hr))
132 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
133 continue;
136 PropVariantInit(&pv);
138 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pv);
139 if(FAILED(hr))
141 PropVariantClear(&pv);
142 IPropertyStore_Release(ps);
143 WARN("GetValue failed: 0x%08lx\n", hr);
144 continue;
146 CLSIDFromString(pv.pwszVal, &devid);
148 PropVariantClear(&pv);
149 IPropertyStore_Release(ps);
151 if(IsEqualGUID(&devid, guid))
153 *out = device;
154 break;
157 IMMDevice_Release(device);
159 hr = ((i==count) ? E_FAIL : S_OK);
161 IMMDeviceCollection_Release(coll);
162 return hr;
166 static void add_device(IMMDevice *device, DevMap *devmap)
168 IPropertyStore *ps;
169 PROPVARIANT pvguid;
170 PROPVARIANT pvname;
171 HRESULT hr;
172 int len;
174 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
175 if(FAILED(hr))
177 WARN("OpenPropertyStore failed: 0x%08lx\n", hr);
178 return;
181 PropVariantInit(&pvguid);
182 PropVariantInit(&pvname);
184 hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pvguid);
185 if(FAILED(hr))
186 WARN("GetValue failed: 0x%08lx\n", hr);
187 else
189 hr = IPropertyStore_GetValue(ps, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pvname);
190 if(FAILED(hr))
191 WARN("GetValue failed: 0x%08lx\n", hr);
193 if(SUCCEEDED(hr))
195 TRACE("Got device \"%ls\", GUID \"%ls\"\n", pvname.pwszVal, pvguid.pwszVal);
196 hr = CLSIDFromString(pvguid.pwszVal, &devmap->guid);
198 if(SUCCEEDED(hr))
200 if((len=WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, NULL, 0, NULL, NULL)) > 0)
202 devmap->name = calloc(1, len);
203 WideCharToMultiByte(CP_ACP, 0, pvname.pwszVal, -1, devmap->name, len, NULL, NULL);
207 PropVariantClear(&pvname);
208 PropVariantClear(&pvguid);
209 IPropertyStore_Release(ps);
212 static DevMap *ProbeDevices(IMMDeviceEnumerator *devenum, EDataFlow flowdir, ALuint *numdevs)
214 IMMDeviceCollection *coll;
215 IMMDevice *defdev = NULL;
216 DevMap *devlist = NULL;
217 HRESULT hr;
218 UINT count;
219 UINT idx;
220 UINT i;
222 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flowdir, DEVICE_STATE_ACTIVE, &coll);
223 if(FAILED(hr))
225 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr);
226 return NULL;
229 idx = count = 0;
230 hr = IMMDeviceCollection_GetCount(coll, &count);
231 if(SUCCEEDED(hr) && count > 0)
233 devlist = calloc(count, sizeof(*devlist));
234 if(!devlist)
236 IMMDeviceCollection_Release(coll);
237 return NULL;
240 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, flowdir,
241 eMultimedia, &defdev);
243 if(SUCCEEDED(hr) && defdev != NULL)
244 add_device(defdev, &devlist[idx++]);
246 for(i = 0;i < count && idx < count;++i)
248 IMMDevice *device;
250 if(FAILED(IMMDeviceCollection_Item(coll, i, &device)))
251 continue;
253 if(device != defdev)
254 add_device(device, &devlist[idx++]);
256 IMMDevice_Release(device);
259 if(defdev) IMMDevice_Release(defdev);
260 IMMDeviceCollection_Release(coll);
262 *numdevs = idx;
263 return devlist;
267 static ALuint MMDevApiProc(ALvoid *ptr)
269 ALCdevice *device = ptr;
270 MMDevApiData *data = device->ExtraData;
271 union {
272 IAudioRenderClient *iface;
273 void *ptr;
274 } render;
275 UINT32 update_size, num_updates;
276 UINT32 written, len;
277 BYTE *buffer;
278 HRESULT hr;
280 hr = CoInitialize(NULL);
281 if(FAILED(hr))
283 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr);
284 aluHandleDisconnect(device);
285 return 0;
288 hr = IAudioClient_GetService(data->client, &IID_IAudioRenderClient, &render.ptr);
289 if(FAILED(hr))
291 ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr);
292 aluHandleDisconnect(device);
293 return 0;
296 SetRTPriority();
298 update_size = device->UpdateSize;
299 num_updates = device->NumUpdates;
300 while(!data->killNow)
302 hr = IAudioClient_GetCurrentPadding(data->client, &written);
303 if(FAILED(hr))
305 ERR("Failed to get padding: 0x%08lx\n", hr);
306 aluHandleDisconnect(device);
307 break;
310 len = update_size*num_updates - written;
311 if(len < update_size)
313 DWORD res;
314 res = WaitForSingleObjectEx(data->hNotifyEvent, 2000, FALSE);
315 if(res != WAIT_OBJECT_0)
316 ERR("WaitForSingleObjectEx error: 0x%lx\n", res);
317 continue;
319 len -= len%update_size;
321 hr = IAudioRenderClient_GetBuffer(render.iface, len, &buffer);
322 if(SUCCEEDED(hr))
324 aluMixData(device, buffer, len);
325 hr = IAudioRenderClient_ReleaseBuffer(render.iface, len, 0);
327 if(FAILED(hr))
329 ERR("Failed to buffer data: 0x%08lx\n", hr);
330 aluHandleDisconnect(device);
331 break;
335 IAudioRenderClient_Release(render.iface);
337 CoUninitialize();
338 return 0;
342 static ALCboolean MakeExtensible(WAVEFORMATEXTENSIBLE *out, const WAVEFORMATEX *in)
344 memset(out, 0, sizeof(*out));
345 if(in->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
346 *out = *(const WAVEFORMATEXTENSIBLE*)in;
347 else if(in->wFormatTag == WAVE_FORMAT_PCM)
349 out->Format = *in;
350 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
351 out->Format.cbSize = sizeof(*out) - sizeof(*in);
352 if(out->Format.nChannels == 1)
353 out->dwChannelMask = MONO;
354 else if(out->Format.nChannels == 2)
355 out->dwChannelMask = STEREO;
356 else
357 ERR("Unhandled PCM channel count: %d\n", out->Format.nChannels);
358 out->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
360 else if(in->wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
362 out->Format = *in;
363 out->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
364 out->Format.cbSize = sizeof(*out) - sizeof(*in);
365 if(out->Format.nChannels == 1)
366 out->dwChannelMask = MONO;
367 else if(out->Format.nChannels == 2)
368 out->dwChannelMask = STEREO;
369 else
370 ERR("Unhandled IEEE float channel count: %d\n", out->Format.nChannels);
371 out->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
373 else
375 ERR("Unhandled format tag: 0x%04x\n", in->wFormatTag);
376 return ALC_FALSE;
378 return ALC_TRUE;
381 static HRESULT DoReset(ALCdevice *device)
383 MMDevApiData *data = device->ExtraData;
384 WAVEFORMATEXTENSIBLE OutputType;
385 WAVEFORMATEX *wfx = NULL;
386 REFERENCE_TIME min_per, buf_time;
387 UINT32 buffer_len, min_len;
388 HRESULT hr;
390 hr = IAudioClient_GetMixFormat(data->client, &wfx);
391 if(FAILED(hr))
393 ERR("Failed to get mix format: 0x%08lx\n", hr);
394 return hr;
397 if(!MakeExtensible(&OutputType, wfx))
399 CoTaskMemFree(wfx);
400 return E_FAIL;
402 CoTaskMemFree(wfx);
403 wfx = NULL;
405 buf_time = ((REFERENCE_TIME)device->UpdateSize*device->NumUpdates*10000000 +
406 device->Frequency-1) / device->Frequency;
408 if(!(device->Flags&DEVICE_FREQUENCY_REQUEST))
409 device->Frequency = OutputType.Format.nSamplesPerSec;
410 if(!(device->Flags&DEVICE_CHANNELS_REQUEST))
412 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
413 device->FmtChans = DevFmtMono;
414 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
415 device->FmtChans = DevFmtStereo;
416 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
417 device->FmtChans = DevFmtQuad;
418 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
419 device->FmtChans = DevFmtX51;
420 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
421 device->FmtChans = DevFmtX51Side;
422 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
423 device->FmtChans = DevFmtX61;
424 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
425 device->FmtChans = DevFmtX71;
426 else
427 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
430 switch(device->FmtChans)
432 case DevFmtMono:
433 OutputType.Format.nChannels = 1;
434 OutputType.dwChannelMask = MONO;
435 break;
436 case DevFmtStereo:
437 OutputType.Format.nChannels = 2;
438 OutputType.dwChannelMask = STEREO;
439 break;
440 case DevFmtQuad:
441 OutputType.Format.nChannels = 4;
442 OutputType.dwChannelMask = QUAD;
443 break;
444 case DevFmtX51:
445 OutputType.Format.nChannels = 6;
446 OutputType.dwChannelMask = X5DOT1;
447 break;
448 case DevFmtX51Side:
449 OutputType.Format.nChannels = 6;
450 OutputType.dwChannelMask = X5DOT1SIDE;
451 break;
452 case DevFmtX61:
453 OutputType.Format.nChannels = 7;
454 OutputType.dwChannelMask = X6DOT1;
455 break;
456 case DevFmtX71:
457 OutputType.Format.nChannels = 8;
458 OutputType.dwChannelMask = X7DOT1;
459 break;
461 switch(device->FmtType)
463 case DevFmtByte:
464 device->FmtType = DevFmtUByte;
465 /* fall-through */
466 case DevFmtUByte:
467 OutputType.Format.wBitsPerSample = 8;
468 OutputType.Samples.wValidBitsPerSample = 8;
469 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
470 break;
471 case DevFmtUShort:
472 device->FmtType = DevFmtShort;
473 /* fall-through */
474 case DevFmtShort:
475 OutputType.Format.wBitsPerSample = 16;
476 OutputType.Samples.wValidBitsPerSample = 16;
477 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
478 break;
479 case DevFmtUInt:
480 device->FmtType = DevFmtInt;
481 /* fall-through */
482 case DevFmtInt:
483 OutputType.Format.wBitsPerSample = 32;
484 OutputType.Samples.wValidBitsPerSample = 32;
485 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
486 break;
487 case DevFmtFloat:
488 OutputType.Format.wBitsPerSample = 32;
489 OutputType.Samples.wValidBitsPerSample = 32;
490 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
491 break;
493 OutputType.Format.nSamplesPerSec = device->Frequency;
495 OutputType.Format.nBlockAlign = OutputType.Format.nChannels *
496 OutputType.Format.wBitsPerSample / 8;
497 OutputType.Format.nAvgBytesPerSec = OutputType.Format.nSamplesPerSec *
498 OutputType.Format.nBlockAlign;
500 hr = IAudioClient_IsFormatSupported(data->client, AUDCLNT_SHAREMODE_SHARED, &OutputType.Format, &wfx);
501 if(FAILED(hr))
503 ERR("Failed to check format support: 0x%08lx\n", hr);
504 hr = IAudioClient_GetMixFormat(data->client, &wfx);
506 if(FAILED(hr))
508 ERR("Failed to find a supported format: 0x%08lx\n", hr);
509 return hr;
512 if(wfx != NULL)
514 if(!MakeExtensible(&OutputType, wfx))
516 CoTaskMemFree(wfx);
517 return E_FAIL;
519 CoTaskMemFree(wfx);
520 wfx = NULL;
522 device->Frequency = OutputType.Format.nSamplesPerSec;
523 if(OutputType.Format.nChannels == 1 && OutputType.dwChannelMask == MONO)
524 device->FmtChans = DevFmtMono;
525 else if(OutputType.Format.nChannels == 2 && OutputType.dwChannelMask == STEREO)
526 device->FmtChans = DevFmtStereo;
527 else if(OutputType.Format.nChannels == 4 && OutputType.dwChannelMask == QUAD)
528 device->FmtChans = DevFmtQuad;
529 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1)
530 device->FmtChans = DevFmtX51;
531 else if(OutputType.Format.nChannels == 6 && OutputType.dwChannelMask == X5DOT1SIDE)
532 device->FmtChans = DevFmtX51Side;
533 else if(OutputType.Format.nChannels == 7 && OutputType.dwChannelMask == X6DOT1)
534 device->FmtChans = DevFmtX61;
535 else if(OutputType.Format.nChannels == 8 && OutputType.dwChannelMask == X7DOT1)
536 device->FmtChans = DevFmtX71;
537 else
539 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType.Format.nChannels, OutputType.dwChannelMask);
540 device->FmtChans = DevFmtStereo;
541 OutputType.Format.nChannels = 2;
542 OutputType.dwChannelMask = STEREO;
545 if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))
547 if(OutputType.Format.wBitsPerSample == 8)
548 device->FmtType = DevFmtUByte;
549 else if(OutputType.Format.wBitsPerSample == 16)
550 device->FmtType = DevFmtShort;
551 else if(OutputType.Format.wBitsPerSample == 32)
552 device->FmtType = DevFmtInt;
553 else
555 device->FmtType = DevFmtShort;
556 OutputType.Format.wBitsPerSample = 16;
559 else if(IsEqualGUID(&OutputType.SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
561 device->FmtType = DevFmtFloat;
562 OutputType.Format.wBitsPerSample = 32;
564 else
566 ERR("Unhandled format sub-type\n");
567 device->FmtType = DevFmtShort;
568 OutputType.Format.wBitsPerSample = 16;
569 OutputType.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
571 OutputType.Samples.wValidBitsPerSample = OutputType.Format.wBitsPerSample;
574 SetDefaultWFXChannelOrder(device);
576 hr = IAudioClient_Initialize(data->client, AUDCLNT_SHAREMODE_SHARED,
577 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
578 buf_time, 0, &OutputType.Format, NULL);
579 if(FAILED(hr))
581 ERR("Failed to initialize audio client: 0x%08lx\n", hr);
582 return hr;
585 hr = IAudioClient_GetDevicePeriod(data->client, &min_per, NULL);
586 if(SUCCEEDED(hr))
588 min_len = (UINT32)((min_per*device->Frequency + 10000000-1) / 10000000);
589 hr = IAudioClient_GetBufferSize(data->client, &buffer_len);
591 if(FAILED(hr))
593 ERR("Failed to get audio buffer info: 0x%08lx\n", hr);
594 return hr;
597 device->UpdateSize = min_len;
598 device->NumUpdates = buffer_len / device->UpdateSize;
599 if(device->NumUpdates <= 1)
601 device->NumUpdates = 1;
602 ERR("Audio client returned buffer_len < period*2; expect break up\n");
605 ResetEvent(data->hNotifyEvent);
606 hr = IAudioClient_SetEventHandle(data->client, data->hNotifyEvent);
607 if(SUCCEEDED(hr))
608 hr = IAudioClient_Start(data->client);
609 if(FAILED(hr))
611 ERR("Failed to start audio client: 0x%08lx\n", hr);
612 return hr;
615 data->thread = StartThread(MMDevApiProc, device);
616 if(!data->thread)
618 IAudioClient_Stop(data->client);
619 ERR("Failed to start thread\n");
620 return E_FAIL;
623 return hr;
627 static DWORD CALLBACK MMDevApiMsgProc(void *ptr)
629 ThreadRequest *req = ptr;
630 IMMDeviceEnumerator *Enumerator;
631 ALuint deviceCount = 0;
632 MMDevApiData *data;
633 ALCdevice *device;
634 HRESULT hr, cohr;
635 MSG msg;
637 TRACE("Starting message thread\n");
639 cohr = CoInitialize(NULL);
640 if(FAILED(cohr))
642 WARN("Failed to initialize COM: 0x%08lx\n", cohr);
643 req->result = cohr;
644 SetEvent(req->FinishedEvt);
645 return 0;
648 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
649 if(FAILED(hr))
651 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr);
652 CoUninitialize();
653 req->result = hr;
654 SetEvent(req->FinishedEvt);
655 return 0;
657 Enumerator = ptr;
658 IMMDeviceEnumerator_Release(Enumerator);
659 Enumerator = NULL;
661 CoUninitialize();
663 req->result = S_OK;
664 SetEvent(req->FinishedEvt);
666 TRACE("Starting message loop\n");
667 while(GetMessage(&msg, NULL, 0, 0))
669 TRACE("Got message %u\n", msg.message);
670 switch(msg.message)
672 case WM_USER_OpenDevice:
673 req = (ThreadRequest*)msg.wParam;
674 device = (ALCdevice*)msg.lParam;
675 data = device->ExtraData;
677 hr = cohr = S_OK;
678 if(++deviceCount == 1)
679 hr = cohr = CoInitialize(NULL);
680 if(SUCCEEDED(hr))
681 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
682 if(SUCCEEDED(hr))
684 Enumerator = ptr;
685 hr = get_mmdevice_by_guid(Enumerator, eRender, &data->guid, &data->mmdev);
686 IMMDeviceEnumerator_Release(Enumerator);
687 Enumerator = NULL;
689 if(SUCCEEDED(hr))
690 hr = IMMDevice_Activate(data->mmdev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, &ptr);
691 if(SUCCEEDED(hr))
692 data->client = ptr;
694 if(FAILED(hr))
696 if(data->mmdev)
697 IMMDevice_Release(data->mmdev);
698 data->mmdev = NULL;
699 if(--deviceCount == 0 && SUCCEEDED(cohr))
700 CoUninitialize();
703 req->result = hr;
704 SetEvent(req->FinishedEvt);
705 continue;
707 case WM_USER_ResetDevice:
708 req = (ThreadRequest*)msg.wParam;
709 device = (ALCdevice*)msg.lParam;
711 req->result = DoReset(device);
712 SetEvent(req->FinishedEvt);
713 continue;
715 case WM_USER_StopDevice:
716 req = (ThreadRequest*)msg.wParam;
717 device = (ALCdevice*)msg.lParam;
718 data = device->ExtraData;
720 if(data->thread)
722 data->killNow = 1;
723 StopThread(data->thread);
724 data->thread = NULL;
726 data->killNow = 0;
728 IAudioClient_Stop(data->client);
731 req->result = S_OK;
732 SetEvent(req->FinishedEvt);
733 continue;
735 case WM_USER_CloseDevice:
736 req = (ThreadRequest*)msg.wParam;
737 device = (ALCdevice*)msg.lParam;
738 data = device->ExtraData;
740 IAudioClient_Release(data->client);
741 data->client = NULL;
743 IMMDevice_Release(data->mmdev);
744 data->mmdev = NULL;
746 if(--deviceCount == 0)
747 CoUninitialize();
749 req->result = S_OK;
750 SetEvent(req->FinishedEvt);
751 continue;
753 case WM_USER_Enumerate:
754 req = (ThreadRequest*)msg.wParam;
756 hr = cohr = S_OK;
757 if(++deviceCount == 1)
758 hr = cohr = CoInitialize(NULL);
759 if(SUCCEEDED(hr))
760 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, &ptr);
761 if(SUCCEEDED(hr))
763 EDataFlow flowdir;
764 DevMap **devlist;
765 ALuint *numdevs;
766 ALuint i;
768 Enumerator = ptr;
769 if(msg.lParam == CAPTURE_DEVICE_PROBE)
771 flowdir = eCapture;
772 devlist = &CaptureDeviceList;
773 numdevs = &NumCaptureDevices;
775 else
777 flowdir = eRender;
778 devlist = &PlaybackDeviceList;
779 numdevs = &NumPlaybackDevices;
782 for(i = 0;i < *numdevs;i++)
783 free((*devlist)[i].name);
784 free(*devlist);
785 *devlist = NULL;
786 *numdevs = 0;
788 *devlist = ProbeDevices(Enumerator, flowdir, numdevs);
790 IMMDeviceEnumerator_Release(Enumerator);
791 Enumerator = NULL;
794 if(--deviceCount == 0 && SUCCEEDED(cohr))
795 CoUninitialize();
797 req->result = S_OK;
798 SetEvent(req->FinishedEvt);
799 continue;
801 default:
802 ERR("Unexpected message: %u\n", msg.message);
803 continue;
806 TRACE("Message loop finished\n");
808 return 0;
812 static BOOL MMDevApiLoad(void)
814 static HRESULT InitResult;
815 if(!ThreadHdl)
817 ThreadRequest req;
818 InitResult = E_FAIL;
820 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
821 if(req.FinishedEvt == NULL)
822 ERR("Failed to create event: %lu\n", GetLastError());
823 else
825 ThreadHdl = CreateThread(NULL, 0, MMDevApiMsgProc, &req, 0, &ThreadID);
826 if(ThreadHdl != NULL)
827 InitResult = WaitForResponse(&req);
828 CloseHandle(req.FinishedEvt);
831 return SUCCEEDED(InitResult);
835 static ALCenum MMDevApiOpenPlayback(ALCdevice *device, const ALCchar *deviceName)
837 MMDevApiData *data = NULL;
838 HRESULT hr;
840 //Initialise requested device
841 data = calloc(1, sizeof(MMDevApiData));
842 if(!data)
843 return ALC_OUT_OF_MEMORY;
844 device->ExtraData = data;
846 hr = S_OK;
847 data->hNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
848 data->MsgEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
849 if(data->hNotifyEvent == NULL || data->MsgEvent == NULL)
850 hr = E_FAIL;
852 if(SUCCEEDED(hr))
854 if(!PlaybackDeviceList)
856 ThreadRequest req = { data->MsgEvent, 0 };
857 if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, ALL_DEVICE_PROBE))
858 (void)WaitForResponse(&req);
861 if(!deviceName && NumPlaybackDevices > 0)
863 deviceName = PlaybackDeviceList[0].name;
864 data->guid = PlaybackDeviceList[0].guid;
866 else
868 ALuint i;
870 hr = E_FAIL;
871 for(i = 0;i < NumPlaybackDevices;i++)
873 if(PlaybackDeviceList[i].name &&
874 strcmp(deviceName, PlaybackDeviceList[i].name) == 0)
876 data->guid = PlaybackDeviceList[i].guid;
877 hr = S_OK;
878 break;
884 if(SUCCEEDED(hr))
886 ThreadRequest req = { data->MsgEvent, 0 };
888 hr = E_FAIL;
889 if(PostThreadMessage(ThreadID, WM_USER_OpenDevice, (WPARAM)&req, (LPARAM)device))
890 hr = WaitForResponse(&req);
893 if(FAILED(hr))
895 if(data->hNotifyEvent != NULL)
896 CloseHandle(data->hNotifyEvent);
897 data->hNotifyEvent = NULL;
898 if(data->MsgEvent != NULL)
899 CloseHandle(data->MsgEvent);
900 data->MsgEvent = NULL;
902 free(data);
903 device->ExtraData = NULL;
905 ERR("Device init failed: 0x%08lx\n", hr);
906 return ALC_INVALID_VALUE;
909 device->szDeviceName = strdup(deviceName);
910 return ALC_NO_ERROR;
913 static void MMDevApiClosePlayback(ALCdevice *device)
915 MMDevApiData *data = device->ExtraData;
916 ThreadRequest req = { data->MsgEvent, 0 };
918 if(PostThreadMessage(ThreadID, WM_USER_CloseDevice, (WPARAM)&req, (LPARAM)device))
919 (void)WaitForResponse(&req);
921 CloseHandle(data->MsgEvent);
922 data->MsgEvent = NULL;
924 CloseHandle(data->hNotifyEvent);
925 data->hNotifyEvent = NULL;
927 free(data);
928 device->ExtraData = NULL;
931 static ALCboolean MMDevApiResetPlayback(ALCdevice *device)
933 MMDevApiData *data = device->ExtraData;
934 ThreadRequest req = { data->MsgEvent, 0 };
935 HRESULT hr = E_FAIL;
937 if(PostThreadMessage(ThreadID, WM_USER_ResetDevice, (WPARAM)&req, (LPARAM)device))
938 hr = WaitForResponse(&req);
940 return SUCCEEDED(hr) ? ALC_TRUE : ALC_FALSE;
943 static void MMDevApiStopPlayback(ALCdevice *device)
945 MMDevApiData *data = device->ExtraData;
946 ThreadRequest req = { data->MsgEvent, 0 };
948 if(PostThreadMessage(ThreadID, WM_USER_StopDevice, (WPARAM)&req, (LPARAM)device))
949 (void)WaitForResponse(&req);
953 static const BackendFuncs MMDevApiFuncs = {
954 MMDevApiOpenPlayback,
955 MMDevApiClosePlayback,
956 MMDevApiResetPlayback,
957 MMDevApiStopPlayback,
958 NULL,
959 NULL,
960 NULL,
961 NULL,
962 NULL,
963 NULL
967 ALCboolean alcMMDevApiInit(BackendFuncs *FuncList)
969 if(!MMDevApiLoad())
970 return ALC_FALSE;
971 *FuncList = MMDevApiFuncs;
972 return ALC_TRUE;
975 void alcMMDevApiDeinit(void)
977 if(ThreadHdl)
979 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID);
980 PostThreadMessage(ThreadID, WM_QUIT, 0, 0);
981 CloseHandle(ThreadHdl);
982 ThreadHdl = NULL;
986 void alcMMDevApiProbe(enum DevProbe type)
988 ThreadRequest req = { NULL, 0 };
989 HRESULT hr = E_FAIL;
991 switch(type)
993 case ALL_DEVICE_PROBE:
994 req.FinishedEvt = CreateEvent(NULL, FALSE, FALSE, NULL);
995 if(req.FinishedEvt == NULL)
996 ERR("Failed to create event: %lu\n", GetLastError());
997 else if(PostThreadMessage(ThreadID, WM_USER_Enumerate, (WPARAM)&req, type))
998 hr = WaitForResponse(&req);
999 if(SUCCEEDED(hr))
1001 ALuint i;
1002 for(i = 0;i < NumPlaybackDevices;i++)
1004 if(PlaybackDeviceList[i].name)
1005 AppendAllDeviceList(PlaybackDeviceList[i].name);
1008 break;
1010 case CAPTURE_DEVICE_PROBE:
1011 break;
1013 if(req.FinishedEvt != NULL)
1014 CloseHandle(req.FinishedEvt);
1015 req.FinishedEvt = NULL;