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>
31 #include <devpropdef.h>
36 #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_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)
64 IAudioRenderClient
*render
;
69 volatile UINT32 Padding
;
81 static DevMap
*PlaybackDeviceList
;
82 static ALuint NumPlaybackDevices
;
83 static DevMap
*CaptureDeviceList
;
84 static ALuint NumCaptureDevices
;
87 static HANDLE ThreadHdl
;
88 static DWORD ThreadID
;
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
)
106 ERR("Message response error: %lu\n", GetLastError());
111 static ALCchar
*get_device_name(IMMDevice
*device
)
113 ALCchar
*name
= NULL
;
119 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
122 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
126 PropVariantInit(&pvname
);
128 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
131 WARN("GetValue failed: 0x%08lx\n", hr
);
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
);
149 static void add_device(IMMDevice
*device
, DevMap
*devmap
)
154 hr
= IMMDevice_GetId(device
, &devid
);
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
;
174 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
177 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
182 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
183 if(SUCCEEDED(hr
) && count
> 0)
185 devlist
= calloc(count
, sizeof(*devlist
));
188 IMMDeviceCollection_Release(coll
);
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
)
202 if(FAILED(IMMDeviceCollection_Item(coll
, i
, &device
)))
206 add_device(device
, &devlist
[idx
++]);
208 IMMDevice_Release(device
);
211 if(defdev
) IMMDevice_Release(defdev
);
212 IMMDeviceCollection_Release(coll
);
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
;
228 hr
= CoInitialize(NULL
);
231 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
232 ALCdevice_Lock(device
);
233 aluHandleDisconnect(device
);
234 ALCdevice_Unlock(device
);
240 update_size
= device
->UpdateSize
;
241 buffer_len
= update_size
* device
->NumUpdates
;
242 while(!data
->killNow
)
244 hr
= IAudioClient_GetCurrentPadding(data
->client
, &written
);
247 ERR("Failed to get padding: 0x%08lx\n", hr
);
248 ALCdevice_Lock(device
);
249 aluHandleDisconnect(device
);
250 ALCdevice_Unlock(device
);
253 data
->Padding
= written
;
255 len
= buffer_len
- written
;
256 if(len
< update_size
)
259 res
= WaitForSingleObjectEx(data
->NotifyEvent
, 2000, FALSE
);
260 if(res
!= WAIT_OBJECT_0
)
261 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
264 len
-= len
%update_size
;
266 hr
= IAudioRenderClient_GetBuffer(data
->render
, len
, &buffer
);
269 ALCdevice_Lock(device
);
270 aluMixData(device
, buffer
, len
);
271 data
->Padding
= written
+ len
;
272 ALCdevice_Unlock(device
);
273 hr
= IAudioRenderClient_ReleaseBuffer(data
->render
, len
, 0);
277 ERR("Failed to buffer data: 0x%08lx\n", hr
);
278 ALCdevice_Lock(device
);
279 aluHandleDisconnect(device
);
280 ALCdevice_Unlock(device
);
291 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
293 memset(out
, 0, sizeof(*out
));
294 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
295 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
296 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
299 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
300 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
301 if(out
->Format
.nChannels
== 1)
302 out
->dwChannelMask
= MONO
;
303 else if(out
->Format
.nChannels
== 2)
304 out
->dwChannelMask
= STEREO
;
306 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
307 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
309 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
312 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
313 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
314 if(out
->Format
.nChannels
== 1)
315 out
->dwChannelMask
= MONO
;
316 else if(out
->Format
.nChannels
== 2)
317 out
->dwChannelMask
= STEREO
;
319 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
320 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
324 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
330 static HRESULT
DoReset(ALCdevice
*device
)
332 MMDevApiData
*data
= device
->ExtraData
;
333 WAVEFORMATEXTENSIBLE OutputType
;
334 WAVEFORMATEX
*wfx
= NULL
;
335 REFERENCE_TIME min_per
, buf_time
;
336 UINT32 buffer_len
, min_len
;
339 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
342 ERR("Failed to get mix format: 0x%08lx\n", hr
);
346 if(!MakeExtensible(&OutputType
, wfx
))
354 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
355 device
->Frequency
-1) / device
->Frequency
;
357 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
358 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
359 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
361 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
362 device
->FmtChans
= DevFmtMono
;
363 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
364 device
->FmtChans
= DevFmtStereo
;
365 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
366 device
->FmtChans
= DevFmtQuad
;
367 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
368 device
->FmtChans
= DevFmtX51
;
369 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
370 device
->FmtChans
= DevFmtX51Side
;
371 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
372 device
->FmtChans
= DevFmtX61
;
373 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
374 device
->FmtChans
= DevFmtX71
;
376 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
379 switch(device
->FmtChans
)
382 OutputType
.Format
.nChannels
= 1;
383 OutputType
.dwChannelMask
= MONO
;
386 OutputType
.Format
.nChannels
= 2;
387 OutputType
.dwChannelMask
= STEREO
;
390 OutputType
.Format
.nChannels
= 4;
391 OutputType
.dwChannelMask
= QUAD
;
394 OutputType
.Format
.nChannels
= 6;
395 OutputType
.dwChannelMask
= X5DOT1
;
398 OutputType
.Format
.nChannels
= 6;
399 OutputType
.dwChannelMask
= X5DOT1SIDE
;
402 OutputType
.Format
.nChannels
= 7;
403 OutputType
.dwChannelMask
= X6DOT1
;
406 OutputType
.Format
.nChannels
= 8;
407 OutputType
.dwChannelMask
= X7DOT1
;
410 switch(device
->FmtType
)
413 device
->FmtType
= DevFmtUByte
;
416 OutputType
.Format
.wBitsPerSample
= 8;
417 OutputType
.Samples
.wValidBitsPerSample
= 8;
418 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
421 device
->FmtType
= DevFmtShort
;
424 OutputType
.Format
.wBitsPerSample
= 16;
425 OutputType
.Samples
.wValidBitsPerSample
= 16;
426 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
429 device
->FmtType
= DevFmtInt
;
432 OutputType
.Format
.wBitsPerSample
= 32;
433 OutputType
.Samples
.wValidBitsPerSample
= 32;
434 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
437 OutputType
.Format
.wBitsPerSample
= 32;
438 OutputType
.Samples
.wValidBitsPerSample
= 32;
439 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
442 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
444 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
445 OutputType
.Format
.wBitsPerSample
/ 8;
446 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
447 OutputType
.Format
.nBlockAlign
;
449 hr
= IAudioClient_IsFormatSupported(data
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
452 ERR("Failed to check format support: 0x%08lx\n", hr
);
453 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
457 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
463 if(!MakeExtensible(&OutputType
, wfx
))
471 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
472 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
473 device
->FmtChans
= DevFmtMono
;
474 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
475 device
->FmtChans
= DevFmtStereo
;
476 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
477 device
->FmtChans
= DevFmtQuad
;
478 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
479 device
->FmtChans
= DevFmtX51
;
480 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
481 device
->FmtChans
= DevFmtX51Side
;
482 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
483 device
->FmtChans
= DevFmtX61
;
484 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
485 device
->FmtChans
= DevFmtX71
;
488 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
489 device
->FmtChans
= DevFmtStereo
;
490 OutputType
.Format
.nChannels
= 2;
491 OutputType
.dwChannelMask
= STEREO
;
494 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
496 if(OutputType
.Format
.wBitsPerSample
== 8)
497 device
->FmtType
= DevFmtUByte
;
498 else if(OutputType
.Format
.wBitsPerSample
== 16)
499 device
->FmtType
= DevFmtShort
;
500 else if(OutputType
.Format
.wBitsPerSample
== 32)
501 device
->FmtType
= DevFmtInt
;
504 device
->FmtType
= DevFmtShort
;
505 OutputType
.Format
.wBitsPerSample
= 16;
508 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
510 device
->FmtType
= DevFmtFloat
;
511 OutputType
.Format
.wBitsPerSample
= 32;
515 ERR("Unhandled format sub-type\n");
516 device
->FmtType
= DevFmtShort
;
517 OutputType
.Format
.wBitsPerSample
= 16;
518 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
520 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
523 SetDefaultWFXChannelOrder(device
);
525 hr
= IAudioClient_Initialize(data
->client
, AUDCLNT_SHAREMODE_SHARED
,
526 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
527 buf_time
, 0, &OutputType
.Format
, NULL
);
530 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
534 hr
= IAudioClient_GetDevicePeriod(data
->client
, &min_per
, NULL
);
537 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
538 /* Find the nearest multiple of the period size to the update size */
539 if(min_len
< device
->UpdateSize
)
540 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
541 hr
= IAudioClient_GetBufferSize(data
->client
, &buffer_len
);
545 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
549 device
->UpdateSize
= min_len
;
550 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
551 if(device
->NumUpdates
<= 1)
553 ERR("Audio client returned buffer_len < period*2; expect break up\n");
554 device
->NumUpdates
= 2;
555 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
562 static DWORD CALLBACK
MMDevApiMsgProc(void *ptr
)
564 ThreadRequest
*req
= ptr
;
565 IMMDeviceEnumerator
*Enumerator
;
566 ALuint deviceCount
= 0;
572 TRACE("Starting message thread\n");
574 cohr
= CoInitialize(NULL
);
577 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
579 SetEvent(req
->FinishedEvt
);
583 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
586 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
589 SetEvent(req
->FinishedEvt
);
593 IMMDeviceEnumerator_Release(Enumerator
);
599 SetEvent(req
->FinishedEvt
);
601 TRACE("Starting message loop\n");
602 while(GetMessage(&msg
, NULL
, 0, 0))
604 TRACE("Got message %u\n", msg
.message
);
607 case WM_USER_OpenDevice
:
608 req
= (ThreadRequest
*)msg
.wParam
;
609 device
= (ALCdevice
*)msg
.lParam
;
610 data
= device
->ExtraData
;
613 if(++deviceCount
== 1)
614 hr
= cohr
= CoInitialize(NULL
);
616 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
621 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &data
->mmdev
);
623 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, data
->devid
, &data
->mmdev
);
624 IMMDeviceEnumerator_Release(Enumerator
);
628 hr
= IMMDevice_Activate(data
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
632 device
->DeviceName
= get_device_name(data
->mmdev
);
638 IMMDevice_Release(data
->mmdev
);
640 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
645 SetEvent(req
->FinishedEvt
);
648 case WM_USER_ResetDevice
:
649 req
= (ThreadRequest
*)msg
.wParam
;
650 device
= (ALCdevice
*)msg
.lParam
;
652 req
->result
= DoReset(device
);
653 SetEvent(req
->FinishedEvt
);
656 case WM_USER_StartDevice
:
657 req
= (ThreadRequest
*)msg
.wParam
;
658 device
= (ALCdevice
*)msg
.lParam
;
659 data
= device
->ExtraData
;
661 ResetEvent(data
->NotifyEvent
);
662 hr
= IAudioClient_SetEventHandle(data
->client
, data
->NotifyEvent
);
664 ERR("Failed to set event handle: 0x%08lx\n", hr
);
667 hr
= IAudioClient_Start(data
->client
);
669 ERR("Failed to start audio client: 0x%08lx\n", hr
);
673 hr
= IAudioClient_GetService(data
->client
, &IID_IAudioRenderClient
, &ptr
);
677 data
->thread
= StartThread(MMDevApiProc
, device
);
681 IAudioRenderClient_Release(data
->render
);
683 IAudioClient_Stop(data
->client
);
684 ERR("Failed to start thread\n");
690 SetEvent(req
->FinishedEvt
);
693 case WM_USER_StopDevice
:
694 req
= (ThreadRequest
*)msg
.wParam
;
695 device
= (ALCdevice
*)msg
.lParam
;
696 data
= device
->ExtraData
;
701 StopThread(data
->thread
);
706 IAudioRenderClient_Release(data
->render
);
708 IAudioClient_Stop(data
->client
);
712 SetEvent(req
->FinishedEvt
);
715 case WM_USER_CloseDevice
:
716 req
= (ThreadRequest
*)msg
.wParam
;
717 device
= (ALCdevice
*)msg
.lParam
;
718 data
= device
->ExtraData
;
720 IAudioClient_Release(data
->client
);
723 IMMDevice_Release(data
->mmdev
);
726 if(--deviceCount
== 0)
730 SetEvent(req
->FinishedEvt
);
733 case WM_USER_Enumerate
:
734 req
= (ThreadRequest
*)msg
.wParam
;
737 if(++deviceCount
== 1)
738 hr
= cohr
= CoInitialize(NULL
);
740 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
749 if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
752 devlist
= &CaptureDeviceList
;
753 numdevs
= &NumCaptureDevices
;
758 devlist
= &PlaybackDeviceList
;
759 numdevs
= &NumPlaybackDevices
;
762 for(i
= 0;i
< *numdevs
;i
++)
764 free((*devlist
)[i
].name
);
765 free((*devlist
)[i
].devid
);
771 *devlist
= ProbeDevices(Enumerator
, flowdir
, numdevs
);
773 IMMDeviceEnumerator_Release(Enumerator
);
777 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
781 SetEvent(req
->FinishedEvt
);
785 ERR("Unexpected message: %u\n", msg
.message
);
789 TRACE("Message loop finished\n");
795 static BOOL
MMDevApiLoad(void)
797 static HRESULT InitResult
;
803 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
804 if(req
.FinishedEvt
== NULL
)
805 ERR("Failed to create event: %lu\n", GetLastError());
808 ThreadHdl
= CreateThread(NULL
, 0, MMDevApiMsgProc
, &req
, 0, &ThreadID
);
809 if(ThreadHdl
!= NULL
)
810 InitResult
= WaitForResponse(&req
);
811 CloseHandle(req
.FinishedEvt
);
814 return SUCCEEDED(InitResult
);
818 static ALCenum
MMDevApiOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
820 MMDevApiData
*data
= NULL
;
823 //Initialise requested device
824 data
= calloc(1, sizeof(MMDevApiData
));
826 return ALC_OUT_OF_MEMORY
;
827 device
->ExtraData
= data
;
830 data
->NotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
831 data
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
832 if(data
->NotifyEvent
== NULL
|| data
->MsgEvent
== NULL
)
841 if(!PlaybackDeviceList
)
843 ThreadRequest req
= { data
->MsgEvent
, 0 };
844 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
845 (void)WaitForResponse(&req
);
849 for(i
= 0;i
< NumPlaybackDevices
;i
++)
851 if(strcmp(deviceName
, PlaybackDeviceList
[i
].name
) == 0)
853 data
->devid
= strdupW(PlaybackDeviceList
[i
].devid
);
863 ThreadRequest req
= { data
->MsgEvent
, 0 };
866 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)device
))
867 hr
= WaitForResponse(&req
);
872 if(data
->NotifyEvent
!= NULL
)
873 CloseHandle(data
->NotifyEvent
);
874 data
->NotifyEvent
= NULL
;
875 if(data
->MsgEvent
!= NULL
)
876 CloseHandle(data
->MsgEvent
);
877 data
->MsgEvent
= NULL
;
880 device
->ExtraData
= NULL
;
882 ERR("Device init failed: 0x%08lx\n", hr
);
883 return ALC_INVALID_VALUE
;
889 static void MMDevApiClosePlayback(ALCdevice
*device
)
891 MMDevApiData
*data
= device
->ExtraData
;
892 ThreadRequest req
= { data
->MsgEvent
, 0 };
894 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)device
))
895 (void)WaitForResponse(&req
);
897 CloseHandle(data
->MsgEvent
);
898 data
->MsgEvent
= NULL
;
900 CloseHandle(data
->NotifyEvent
);
901 data
->NotifyEvent
= NULL
;
907 device
->ExtraData
= NULL
;
910 static ALCboolean
MMDevApiResetPlayback(ALCdevice
*device
)
912 MMDevApiData
*data
= device
->ExtraData
;
913 ThreadRequest req
= { data
->MsgEvent
, 0 };
916 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)device
))
917 hr
= WaitForResponse(&req
);
919 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
922 static ALCboolean
MMDevApiStartPlayback(ALCdevice
*device
)
924 MMDevApiData
*data
= device
->ExtraData
;
925 ThreadRequest req
= { data
->MsgEvent
, 0 };
928 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)device
))
929 hr
= WaitForResponse(&req
);
931 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
934 static void MMDevApiStopPlayback(ALCdevice
*device
)
936 MMDevApiData
*data
= device
->ExtraData
;
937 ThreadRequest req
= { data
->MsgEvent
, 0 };
939 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)device
))
940 (void)WaitForResponse(&req
);
944 static ALint64
MMDevApiGetLatency(ALCdevice
*device
)
946 MMDevApiData
*data
= device
->ExtraData
;
948 return (ALint64
)data
->Padding
* 1000000000 / device
->Frequency
;
952 static const BackendFuncs MMDevApiFuncs
= {
953 MMDevApiOpenPlayback
,
954 MMDevApiClosePlayback
,
955 MMDevApiResetPlayback
,
956 MMDevApiStartPlayback
,
957 MMDevApiStopPlayback
,
964 ALCdevice_LockDefault
,
965 ALCdevice_UnlockDefault
,
970 ALCboolean
alcMMDevApiInit(BackendFuncs
*FuncList
)
974 *FuncList
= MMDevApiFuncs
;
978 void alcMMDevApiDeinit(void)
982 for(i
= 0;i
< NumPlaybackDevices
;i
++)
984 free(PlaybackDeviceList
[i
].name
);
985 free(PlaybackDeviceList
[i
].devid
);
987 free(PlaybackDeviceList
);
988 PlaybackDeviceList
= NULL
;
989 NumPlaybackDevices
= 0;
991 for(i
= 0;i
< NumCaptureDevices
;i
++)
993 free(CaptureDeviceList
[i
].name
);
994 free(CaptureDeviceList
[i
].devid
);
996 free(CaptureDeviceList
);
997 CaptureDeviceList
= NULL
;
998 NumCaptureDevices
= 0;
1002 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
1003 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
1004 CloseHandle(ThreadHdl
);
1009 void alcMMDevApiProbe(enum DevProbe type
)
1011 ThreadRequest req
= { NULL
, 0 };
1012 HRESULT hr
= E_FAIL
;
1016 case ALL_DEVICE_PROBE
:
1017 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1018 if(req
.FinishedEvt
== NULL
)
1019 ERR("Failed to create event: %lu\n", GetLastError());
1020 else if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
1021 hr
= WaitForResponse(&req
);
1025 for(i
= 0;i
< NumPlaybackDevices
;i
++)
1027 if(PlaybackDeviceList
[i
].name
)
1028 AppendAllDevicesList(PlaybackDeviceList
[i
].name
);
1033 case CAPTURE_DEVICE_PROBE
:
1036 if(req
.FinishedEvt
!= NULL
)
1037 CloseHandle(req
.FinishedEvt
);
1038 req
.FinishedEvt
= NULL
;