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
28 #include <mmdeviceapi.h>
29 #include <audioclient.h>
35 #ifndef _WAVEFORMATEXTENSIBLE_
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)
75 static DevMap
*PlaybackDeviceList
;
76 static ALuint NumPlaybackDevices
;
77 static DevMap
*CaptureDeviceList
;
78 static ALuint NumCaptureDevices
;
81 static HANDLE ThreadHdl
;
82 static DWORD ThreadID
;
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
)
99 ERR("Message response error: %lu\n", GetLastError());
104 static HRESULT
get_mmdevice_by_guid(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, const GUID
*guid
, IMMDevice
**out
)
106 IMMDeviceCollection
*coll
;
110 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
113 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
118 IMMDeviceCollection_GetCount(coll
, &count
);
119 for(i
= 0;i
< count
;++i
)
126 if(FAILED(IMMDeviceCollection_Item(coll
, i
, &device
)))
129 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
132 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
136 PropVariantInit(&pv
);
138 hr
= IPropertyStore_GetValue(ps
, &PKEY_AudioEndpoint_GUID
, &pv
);
141 PropVariantClear(&pv
);
142 IPropertyStore_Release(ps
);
143 WARN("GetValue failed: 0x%08lx\n", hr
);
146 CLSIDFromString(pv
.pwszVal
, &devid
);
148 PropVariantClear(&pv
);
149 IPropertyStore_Release(ps
);
151 if(IsEqualGUID(&devid
, guid
))
157 IMMDevice_Release(device
);
159 hr
= ((i
==count
) ? E_FAIL
: S_OK
);
161 IMMDeviceCollection_Release(coll
);
166 static void add_device(IMMDevice
*device
, DevMap
*devmap
)
174 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
177 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
181 PropVariantInit(&pvguid
);
182 PropVariantInit(&pvname
);
184 hr
= IPropertyStore_GetValue(ps
, &PKEY_AudioEndpoint_GUID
, &pvguid
);
186 WARN("GetValue failed: 0x%08lx\n", hr
);
189 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
191 WARN("GetValue failed: 0x%08lx\n", hr
);
195 TRACE("Got device \"%ls\", GUID \"%ls\"\n", pvname
.pwszVal
, pvguid
.pwszVal
);
196 hr
= CLSIDFromString(pvguid
.pwszVal
, &devmap
->guid
);
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
;
222 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
225 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
230 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
231 if(SUCCEEDED(hr
) && count
> 0)
233 devlist
= calloc(count
, sizeof(*devlist
));
236 IMMDeviceCollection_Release(coll
);
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
)
250 if(FAILED(IMMDeviceCollection_Item(coll
, i
, &device
)))
254 add_device(device
, &devlist
[idx
++]);
256 IMMDevice_Release(device
);
259 if(defdev
) IMMDevice_Release(defdev
);
260 IMMDeviceCollection_Release(coll
);
267 static ALuint
MMDevApiProc(ALvoid
*ptr
)
269 ALCdevice
*device
= ptr
;
270 MMDevApiData
*data
= device
->ExtraData
;
272 IAudioRenderClient
*iface
;
275 UINT32 update_size
, num_updates
;
280 hr
= CoInitialize(NULL
);
283 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
284 aluHandleDisconnect(device
);
288 hr
= IAudioClient_GetService(data
->client
, &IID_IAudioRenderClient
, &render
.ptr
);
291 ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr
);
292 aluHandleDisconnect(device
);
298 update_size
= device
->UpdateSize
;
299 num_updates
= device
->NumUpdates
;
300 while(!data
->killNow
)
302 hr
= IAudioClient_GetCurrentPadding(data
->client
, &written
);
305 ERR("Failed to get padding: 0x%08lx\n", hr
);
306 aluHandleDisconnect(device
);
310 len
= update_size
*num_updates
- written
;
311 if(len
< update_size
)
314 res
= WaitForSingleObjectEx(data
->hNotifyEvent
, 2000, FALSE
);
315 if(res
!= WAIT_OBJECT_0
)
316 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
319 len
-= len
%update_size
;
321 hr
= IAudioRenderClient_GetBuffer(render
.iface
, len
, &buffer
);
324 aluMixData(device
, buffer
, len
);
325 hr
= IAudioRenderClient_ReleaseBuffer(render
.iface
, len
, 0);
329 ERR("Failed to buffer data: 0x%08lx\n", hr
);
330 aluHandleDisconnect(device
);
335 IAudioRenderClient_Release(render
.iface
);
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
)
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
;
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
)
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
;
370 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
371 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
375 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
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
;
390 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
393 ERR("Failed to get mix format: 0x%08lx\n", hr
);
397 if(!MakeExtensible(&OutputType
, wfx
))
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
;
427 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
430 switch(device
->FmtChans
)
433 OutputType
.Format
.nChannels
= 1;
434 OutputType
.dwChannelMask
= MONO
;
437 OutputType
.Format
.nChannels
= 2;
438 OutputType
.dwChannelMask
= STEREO
;
441 OutputType
.Format
.nChannels
= 4;
442 OutputType
.dwChannelMask
= QUAD
;
445 OutputType
.Format
.nChannels
= 6;
446 OutputType
.dwChannelMask
= X5DOT1
;
449 OutputType
.Format
.nChannels
= 6;
450 OutputType
.dwChannelMask
= X5DOT1SIDE
;
453 OutputType
.Format
.nChannels
= 7;
454 OutputType
.dwChannelMask
= X6DOT1
;
457 OutputType
.Format
.nChannels
= 8;
458 OutputType
.dwChannelMask
= X7DOT1
;
461 switch(device
->FmtType
)
464 device
->FmtType
= DevFmtUByte
;
467 OutputType
.Format
.wBitsPerSample
= 8;
468 OutputType
.Samples
.wValidBitsPerSample
= 8;
469 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
472 device
->FmtType
= DevFmtShort
;
475 OutputType
.Format
.wBitsPerSample
= 16;
476 OutputType
.Samples
.wValidBitsPerSample
= 16;
477 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
480 device
->FmtType
= DevFmtInt
;
483 OutputType
.Format
.wBitsPerSample
= 32;
484 OutputType
.Samples
.wValidBitsPerSample
= 32;
485 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
488 OutputType
.Format
.wBitsPerSample
= 32;
489 OutputType
.Samples
.wValidBitsPerSample
= 32;
490 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
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
);
503 ERR("Failed to check format support: 0x%08lx\n", hr
);
504 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
508 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
514 if(!MakeExtensible(&OutputType
, wfx
))
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
;
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
;
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;
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
);
581 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
585 hr
= IAudioClient_GetDevicePeriod(data
->client
, &min_per
, NULL
);
588 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
589 hr
= IAudioClient_GetBufferSize(data
->client
, &buffer_len
);
593 ERR("Failed to get audio buffer info: 0x%08lx\n", 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
);
608 hr
= IAudioClient_Start(data
->client
);
611 ERR("Failed to start audio client: 0x%08lx\n", hr
);
615 data
->thread
= StartThread(MMDevApiProc
, device
);
618 IAudioClient_Stop(data
->client
);
619 ERR("Failed to start thread\n");
627 static DWORD CALLBACK
MMDevApiMsgProc(void *ptr
)
629 ThreadRequest
*req
= ptr
;
630 IMMDeviceEnumerator
*Enumerator
;
631 ALuint deviceCount
= 0;
637 TRACE("Starting message thread\n");
639 cohr
= CoInitialize(NULL
);
642 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
644 SetEvent(req
->FinishedEvt
);
648 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
651 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
654 SetEvent(req
->FinishedEvt
);
658 IMMDeviceEnumerator_Release(Enumerator
);
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
);
672 case WM_USER_OpenDevice
:
673 req
= (ThreadRequest
*)msg
.wParam
;
674 device
= (ALCdevice
*)msg
.lParam
;
675 data
= device
->ExtraData
;
678 if(++deviceCount
== 1)
679 hr
= cohr
= CoInitialize(NULL
);
681 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
685 hr
= get_mmdevice_by_guid(Enumerator
, eRender
, &data
->guid
, &data
->mmdev
);
686 IMMDeviceEnumerator_Release(Enumerator
);
690 hr
= IMMDevice_Activate(data
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
697 IMMDevice_Release(data
->mmdev
);
699 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
704 SetEvent(req
->FinishedEvt
);
707 case WM_USER_ResetDevice
:
708 req
= (ThreadRequest
*)msg
.wParam
;
709 device
= (ALCdevice
*)msg
.lParam
;
711 req
->result
= DoReset(device
);
712 SetEvent(req
->FinishedEvt
);
715 case WM_USER_StopDevice
:
716 req
= (ThreadRequest
*)msg
.wParam
;
717 device
= (ALCdevice
*)msg
.lParam
;
718 data
= device
->ExtraData
;
723 StopThread(data
->thread
);
728 IAudioClient_Stop(data
->client
);
732 SetEvent(req
->FinishedEvt
);
735 case WM_USER_CloseDevice
:
736 req
= (ThreadRequest
*)msg
.wParam
;
737 device
= (ALCdevice
*)msg
.lParam
;
738 data
= device
->ExtraData
;
740 IAudioClient_Release(data
->client
);
743 IMMDevice_Release(data
->mmdev
);
746 if(--deviceCount
== 0)
750 SetEvent(req
->FinishedEvt
);
753 case WM_USER_Enumerate
:
754 req
= (ThreadRequest
*)msg
.wParam
;
757 if(++deviceCount
== 1)
758 hr
= cohr
= CoInitialize(NULL
);
760 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
769 if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
772 devlist
= &CaptureDeviceList
;
773 numdevs
= &NumCaptureDevices
;
778 devlist
= &PlaybackDeviceList
;
779 numdevs
= &NumPlaybackDevices
;
782 for(i
= 0;i
< *numdevs
;i
++)
783 free((*devlist
)[i
].name
);
788 *devlist
= ProbeDevices(Enumerator
, flowdir
, numdevs
);
790 IMMDeviceEnumerator_Release(Enumerator
);
794 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
798 SetEvent(req
->FinishedEvt
);
802 ERR("Unexpected message: %u\n", msg
.message
);
806 TRACE("Message loop finished\n");
812 static BOOL
MMDevApiLoad(void)
814 static HRESULT InitResult
;
820 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
821 if(req
.FinishedEvt
== NULL
)
822 ERR("Failed to create event: %lu\n", GetLastError());
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
;
840 //Initialise requested device
841 data
= calloc(1, sizeof(MMDevApiData
));
843 return ALC_OUT_OF_MEMORY
;
844 device
->ExtraData
= data
;
847 data
->hNotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
848 data
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
849 if(data
->hNotifyEvent
== NULL
|| data
->MsgEvent
== NULL
)
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
;
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
;
886 ThreadRequest req
= { data
->MsgEvent
, 0 };
889 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)device
))
890 hr
= WaitForResponse(&req
);
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
;
903 device
->ExtraData
= NULL
;
905 ERR("Device init failed: 0x%08lx\n", hr
);
906 return ALC_INVALID_VALUE
;
909 device
->szDeviceName
= strdup(deviceName
);
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
;
928 device
->ExtraData
= NULL
;
931 static ALCboolean
MMDevApiResetPlayback(ALCdevice
*device
)
933 MMDevApiData
*data
= device
->ExtraData
;
934 ThreadRequest req
= { data
->MsgEvent
, 0 };
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
,
967 ALCboolean
alcMMDevApiInit(BackendFuncs
*FuncList
)
971 *FuncList
= MMDevApiFuncs
;
975 void alcMMDevApiDeinit(void)
979 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
980 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
981 CloseHandle(ThreadHdl
);
986 void alcMMDevApiProbe(enum DevProbe type
)
988 ThreadRequest req
= { NULL
, 0 };
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
);
1002 for(i
= 0;i
< NumPlaybackDevices
;i
++)
1004 if(PlaybackDeviceList
[i
].name
)
1005 AppendAllDeviceList(PlaybackDeviceList
[i
].name
);
1010 case CAPTURE_DEVICE_PROBE
:
1013 if(req
.FinishedEvt
!= NULL
)
1014 CloseHandle(req
.FinishedEvt
);
1015 req
.FinishedEvt
= NULL
;