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_First (WM_USER+0)
96 #define WM_USER_OpenDevice (WM_USER+0)
97 #define WM_USER_ResetDevice (WM_USER+1)
98 #define WM_USER_StartDevice (WM_USER+2)
99 #define WM_USER_StopDevice (WM_USER+3)
100 #define WM_USER_CloseDevice (WM_USER+4)
101 #define WM_USER_Enumerate (WM_USER+5)
102 #define WM_USER_Last (WM_USER+5)
104 static HRESULT
WaitForResponse(ThreadRequest
*req
)
106 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
108 ERR("Message response error: %lu\n", GetLastError());
113 static ALCchar
*get_device_name(IMMDevice
*device
)
115 ALCchar
*name
= NULL
;
121 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
124 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
128 PropVariantInit(&pvname
);
130 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
133 WARN("GetValue failed: 0x%08lx\n", hr
);
138 if((len
=WideCharToMultiByte(CP_ACP
, 0, pvname
.pwszVal
, -1, NULL
, 0, NULL
, NULL
)) > 0)
140 name
= calloc(1, len
);
141 WideCharToMultiByte(CP_ACP
, 0, pvname
.pwszVal
, -1, name
, len
, NULL
, NULL
);
145 PropVariantClear(&pvname
);
146 IPropertyStore_Release(ps
);
151 static void add_device(IMMDevice
*device
, DevMap
*devmap
)
156 hr
= IMMDevice_GetId(device
, &devid
);
159 devmap
->devid
= strdupW(devid
);
160 devmap
->name
= get_device_name(device
);
161 TRACE("Got device \"%s\", \"%ls\"\n", devmap
->name
, devmap
->devid
);
162 CoTaskMemFree(devid
);
166 static DevMap
*ProbeDevices(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, ALuint
*numdevs
)
168 IMMDeviceCollection
*coll
;
169 IMMDevice
*defdev
= NULL
;
170 DevMap
*devlist
= NULL
;
176 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
179 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
184 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
185 if(SUCCEEDED(hr
) && count
> 0)
187 devlist
= calloc(count
, sizeof(*devlist
));
190 IMMDeviceCollection_Release(coll
);
194 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, flowdir
,
195 eMultimedia
, &defdev
);
197 if(SUCCEEDED(hr
) && defdev
!= NULL
)
198 add_device(defdev
, &devlist
[idx
++]);
200 for(i
= 0;i
< count
&& idx
< count
;++i
)
204 if(FAILED(IMMDeviceCollection_Item(coll
, i
, &device
)))
208 add_device(device
, &devlist
[idx
++]);
210 IMMDevice_Release(device
);
213 if(defdev
) IMMDevice_Release(defdev
);
214 IMMDeviceCollection_Release(coll
);
221 static ALuint
MMDevApiProc(ALvoid
*ptr
)
223 ALCdevice
*device
= ptr
;
224 MMDevApiData
*data
= device
->ExtraData
;
225 UINT32 buffer_len
, written
;
226 ALuint update_size
, len
;
230 hr
= CoInitialize(NULL
);
233 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
234 ALCdevice_Lock(device
);
235 aluHandleDisconnect(device
);
236 ALCdevice_Unlock(device
);
242 update_size
= device
->UpdateSize
;
243 buffer_len
= update_size
* device
->NumUpdates
;
244 while(!data
->killNow
)
246 hr
= IAudioClient_GetCurrentPadding(data
->client
, &written
);
249 ERR("Failed to get padding: 0x%08lx\n", hr
);
250 ALCdevice_Lock(device
);
251 aluHandleDisconnect(device
);
252 ALCdevice_Unlock(device
);
255 data
->Padding
= written
;
257 len
= buffer_len
- written
;
258 if(len
< update_size
)
261 res
= WaitForSingleObjectEx(data
->NotifyEvent
, 2000, FALSE
);
262 if(res
!= WAIT_OBJECT_0
)
263 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
266 len
-= len
%update_size
;
268 hr
= IAudioRenderClient_GetBuffer(data
->render
, len
, &buffer
);
271 ALCdevice_Lock(device
);
272 aluMixData(device
, buffer
, len
);
273 data
->Padding
= written
+ len
;
274 ALCdevice_Unlock(device
);
275 hr
= IAudioRenderClient_ReleaseBuffer(data
->render
, len
, 0);
279 ERR("Failed to buffer data: 0x%08lx\n", hr
);
280 ALCdevice_Lock(device
);
281 aluHandleDisconnect(device
);
282 ALCdevice_Unlock(device
);
293 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
295 memset(out
, 0, sizeof(*out
));
296 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
297 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
298 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
301 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
302 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
303 if(out
->Format
.nChannels
== 1)
304 out
->dwChannelMask
= MONO
;
305 else if(out
->Format
.nChannels
== 2)
306 out
->dwChannelMask
= STEREO
;
308 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
309 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
311 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
314 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
315 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
316 if(out
->Format
.nChannels
== 1)
317 out
->dwChannelMask
= MONO
;
318 else if(out
->Format
.nChannels
== 2)
319 out
->dwChannelMask
= STEREO
;
321 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
322 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
326 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
332 static HRESULT
DoReset(ALCdevice
*device
)
334 MMDevApiData
*data
= device
->ExtraData
;
335 WAVEFORMATEXTENSIBLE OutputType
;
336 WAVEFORMATEX
*wfx
= NULL
;
337 REFERENCE_TIME min_per
, buf_time
;
338 UINT32 buffer_len
, min_len
;
341 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
344 ERR("Failed to get mix format: 0x%08lx\n", hr
);
348 if(!MakeExtensible(&OutputType
, wfx
))
356 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
357 device
->Frequency
-1) / device
->Frequency
;
359 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
360 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
361 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
363 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
364 device
->FmtChans
= DevFmtMono
;
365 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
366 device
->FmtChans
= DevFmtStereo
;
367 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
368 device
->FmtChans
= DevFmtQuad
;
369 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
370 device
->FmtChans
= DevFmtX51
;
371 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
372 device
->FmtChans
= DevFmtX51Side
;
373 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
374 device
->FmtChans
= DevFmtX61
;
375 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
376 device
->FmtChans
= DevFmtX71
;
378 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
381 switch(device
->FmtChans
)
384 OutputType
.Format
.nChannels
= 1;
385 OutputType
.dwChannelMask
= MONO
;
388 OutputType
.Format
.nChannels
= 2;
389 OutputType
.dwChannelMask
= STEREO
;
392 OutputType
.Format
.nChannels
= 4;
393 OutputType
.dwChannelMask
= QUAD
;
396 OutputType
.Format
.nChannels
= 6;
397 OutputType
.dwChannelMask
= X5DOT1
;
400 OutputType
.Format
.nChannels
= 6;
401 OutputType
.dwChannelMask
= X5DOT1SIDE
;
404 OutputType
.Format
.nChannels
= 7;
405 OutputType
.dwChannelMask
= X6DOT1
;
408 OutputType
.Format
.nChannels
= 8;
409 OutputType
.dwChannelMask
= X7DOT1
;
412 switch(device
->FmtType
)
415 device
->FmtType
= DevFmtUByte
;
418 OutputType
.Format
.wBitsPerSample
= 8;
419 OutputType
.Samples
.wValidBitsPerSample
= 8;
420 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
423 device
->FmtType
= DevFmtShort
;
426 OutputType
.Format
.wBitsPerSample
= 16;
427 OutputType
.Samples
.wValidBitsPerSample
= 16;
428 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
431 device
->FmtType
= DevFmtInt
;
434 OutputType
.Format
.wBitsPerSample
= 32;
435 OutputType
.Samples
.wValidBitsPerSample
= 32;
436 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
439 OutputType
.Format
.wBitsPerSample
= 32;
440 OutputType
.Samples
.wValidBitsPerSample
= 32;
441 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
444 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
446 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
447 OutputType
.Format
.wBitsPerSample
/ 8;
448 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
449 OutputType
.Format
.nBlockAlign
;
451 hr
= IAudioClient_IsFormatSupported(data
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
454 ERR("Failed to check format support: 0x%08lx\n", hr
);
455 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
459 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
465 if(!MakeExtensible(&OutputType
, wfx
))
473 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
474 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
475 device
->FmtChans
= DevFmtMono
;
476 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
477 device
->FmtChans
= DevFmtStereo
;
478 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
479 device
->FmtChans
= DevFmtQuad
;
480 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
481 device
->FmtChans
= DevFmtX51
;
482 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
483 device
->FmtChans
= DevFmtX51Side
;
484 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
485 device
->FmtChans
= DevFmtX61
;
486 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
487 device
->FmtChans
= DevFmtX71
;
490 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
491 device
->FmtChans
= DevFmtStereo
;
492 OutputType
.Format
.nChannels
= 2;
493 OutputType
.dwChannelMask
= STEREO
;
496 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
498 if(OutputType
.Format
.wBitsPerSample
== 8)
499 device
->FmtType
= DevFmtUByte
;
500 else if(OutputType
.Format
.wBitsPerSample
== 16)
501 device
->FmtType
= DevFmtShort
;
502 else if(OutputType
.Format
.wBitsPerSample
== 32)
503 device
->FmtType
= DevFmtInt
;
506 device
->FmtType
= DevFmtShort
;
507 OutputType
.Format
.wBitsPerSample
= 16;
510 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
512 device
->FmtType
= DevFmtFloat
;
513 OutputType
.Format
.wBitsPerSample
= 32;
517 ERR("Unhandled format sub-type\n");
518 device
->FmtType
= DevFmtShort
;
519 OutputType
.Format
.wBitsPerSample
= 16;
520 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
522 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
525 SetDefaultWFXChannelOrder(device
);
527 hr
= IAudioClient_Initialize(data
->client
, AUDCLNT_SHAREMODE_SHARED
,
528 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
529 buf_time
, 0, &OutputType
.Format
, NULL
);
532 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
536 hr
= IAudioClient_GetDevicePeriod(data
->client
, &min_per
, NULL
);
539 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
540 /* Find the nearest multiple of the period size to the update size */
541 if(min_len
< device
->UpdateSize
)
542 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
543 hr
= IAudioClient_GetBufferSize(data
->client
, &buffer_len
);
547 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
551 device
->UpdateSize
= min_len
;
552 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
553 if(device
->NumUpdates
<= 1)
555 ERR("Audio client returned buffer_len < period*2; expect break up\n");
556 device
->NumUpdates
= 2;
557 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
564 static DWORD CALLBACK
MMDevApiMsgProc(void *ptr
)
566 ThreadRequest
*req
= ptr
;
567 IMMDeviceEnumerator
*Enumerator
;
568 ALuint deviceCount
= 0;
574 TRACE("Starting message thread\n");
576 cohr
= CoInitialize(NULL
);
579 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
581 SetEvent(req
->FinishedEvt
);
585 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
588 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
591 SetEvent(req
->FinishedEvt
);
595 IMMDeviceEnumerator_Release(Enumerator
);
601 SetEvent(req
->FinishedEvt
);
603 TRACE("Starting message loop\n");
604 while(GetMessage(&msg
, NULL
, WM_USER_First
, WM_USER_Last
))
606 TRACE("Got message %u\n", msg
.message
);
609 case WM_USER_OpenDevice
:
610 req
= (ThreadRequest
*)msg
.wParam
;
611 device
= (ALCdevice
*)msg
.lParam
;
612 data
= device
->ExtraData
;
615 if(++deviceCount
== 1)
616 hr
= cohr
= CoInitialize(NULL
);
618 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
623 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &data
->mmdev
);
625 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, data
->devid
, &data
->mmdev
);
626 IMMDeviceEnumerator_Release(Enumerator
);
630 hr
= IMMDevice_Activate(data
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
634 device
->DeviceName
= get_device_name(data
->mmdev
);
640 IMMDevice_Release(data
->mmdev
);
642 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
647 SetEvent(req
->FinishedEvt
);
650 case WM_USER_ResetDevice
:
651 req
= (ThreadRequest
*)msg
.wParam
;
652 device
= (ALCdevice
*)msg
.lParam
;
654 req
->result
= DoReset(device
);
655 SetEvent(req
->FinishedEvt
);
658 case WM_USER_StartDevice
:
659 req
= (ThreadRequest
*)msg
.wParam
;
660 device
= (ALCdevice
*)msg
.lParam
;
661 data
= device
->ExtraData
;
663 ResetEvent(data
->NotifyEvent
);
664 hr
= IAudioClient_SetEventHandle(data
->client
, data
->NotifyEvent
);
666 ERR("Failed to set event handle: 0x%08lx\n", hr
);
669 hr
= IAudioClient_Start(data
->client
);
671 ERR("Failed to start audio client: 0x%08lx\n", hr
);
675 hr
= IAudioClient_GetService(data
->client
, &IID_IAudioRenderClient
, &ptr
);
679 data
->thread
= StartThread(MMDevApiProc
, device
);
683 IAudioRenderClient_Release(data
->render
);
685 IAudioClient_Stop(data
->client
);
686 ERR("Failed to start thread\n");
692 SetEvent(req
->FinishedEvt
);
695 case WM_USER_StopDevice
:
696 req
= (ThreadRequest
*)msg
.wParam
;
697 device
= (ALCdevice
*)msg
.lParam
;
698 data
= device
->ExtraData
;
703 StopThread(data
->thread
);
708 IAudioRenderClient_Release(data
->render
);
710 IAudioClient_Stop(data
->client
);
714 SetEvent(req
->FinishedEvt
);
717 case WM_USER_CloseDevice
:
718 req
= (ThreadRequest
*)msg
.wParam
;
719 device
= (ALCdevice
*)msg
.lParam
;
720 data
= device
->ExtraData
;
722 IAudioClient_Release(data
->client
);
725 IMMDevice_Release(data
->mmdev
);
728 if(--deviceCount
== 0)
732 SetEvent(req
->FinishedEvt
);
735 case WM_USER_Enumerate
:
736 req
= (ThreadRequest
*)msg
.wParam
;
739 if(++deviceCount
== 1)
740 hr
= cohr
= CoInitialize(NULL
);
742 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
751 if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
754 devlist
= &CaptureDeviceList
;
755 numdevs
= &NumCaptureDevices
;
760 devlist
= &PlaybackDeviceList
;
761 numdevs
= &NumPlaybackDevices
;
764 for(i
= 0;i
< *numdevs
;i
++)
766 free((*devlist
)[i
].name
);
767 free((*devlist
)[i
].devid
);
773 *devlist
= ProbeDevices(Enumerator
, flowdir
, numdevs
);
775 IMMDeviceEnumerator_Release(Enumerator
);
779 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
783 SetEvent(req
->FinishedEvt
);
787 ERR("Unexpected message: %u\n", msg
.message
);
791 TRACE("Message loop finished\n");
797 static BOOL
MMDevApiLoad(void)
799 static HRESULT InitResult
;
805 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
806 if(req
.FinishedEvt
== NULL
)
807 ERR("Failed to create event: %lu\n", GetLastError());
810 ThreadHdl
= CreateThread(NULL
, 0, MMDevApiMsgProc
, &req
, 0, &ThreadID
);
811 if(ThreadHdl
!= NULL
)
812 InitResult
= WaitForResponse(&req
);
813 CloseHandle(req
.FinishedEvt
);
816 return SUCCEEDED(InitResult
);
820 static ALCenum
MMDevApiOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
822 MMDevApiData
*data
= NULL
;
825 //Initialise requested device
826 data
= calloc(1, sizeof(MMDevApiData
));
828 return ALC_OUT_OF_MEMORY
;
829 device
->ExtraData
= data
;
832 data
->NotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
833 data
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
834 if(data
->NotifyEvent
== NULL
|| data
->MsgEvent
== NULL
)
843 if(!PlaybackDeviceList
)
845 ThreadRequest req
= { data
->MsgEvent
, 0 };
846 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
847 (void)WaitForResponse(&req
);
851 for(i
= 0;i
< NumPlaybackDevices
;i
++)
853 if(strcmp(deviceName
, PlaybackDeviceList
[i
].name
) == 0)
855 data
->devid
= strdupW(PlaybackDeviceList
[i
].devid
);
865 ThreadRequest req
= { data
->MsgEvent
, 0 };
868 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)device
))
869 hr
= WaitForResponse(&req
);
874 if(data
->NotifyEvent
!= NULL
)
875 CloseHandle(data
->NotifyEvent
);
876 data
->NotifyEvent
= NULL
;
877 if(data
->MsgEvent
!= NULL
)
878 CloseHandle(data
->MsgEvent
);
879 data
->MsgEvent
= NULL
;
882 device
->ExtraData
= NULL
;
884 ERR("Device init failed: 0x%08lx\n", hr
);
885 return ALC_INVALID_VALUE
;
891 static void MMDevApiClosePlayback(ALCdevice
*device
)
893 MMDevApiData
*data
= device
->ExtraData
;
894 ThreadRequest req
= { data
->MsgEvent
, 0 };
896 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)device
))
897 (void)WaitForResponse(&req
);
899 CloseHandle(data
->MsgEvent
);
900 data
->MsgEvent
= NULL
;
902 CloseHandle(data
->NotifyEvent
);
903 data
->NotifyEvent
= NULL
;
909 device
->ExtraData
= NULL
;
912 static ALCboolean
MMDevApiResetPlayback(ALCdevice
*device
)
914 MMDevApiData
*data
= device
->ExtraData
;
915 ThreadRequest req
= { data
->MsgEvent
, 0 };
918 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)device
))
919 hr
= WaitForResponse(&req
);
921 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
924 static ALCboolean
MMDevApiStartPlayback(ALCdevice
*device
)
926 MMDevApiData
*data
= device
->ExtraData
;
927 ThreadRequest req
= { data
->MsgEvent
, 0 };
930 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)device
))
931 hr
= WaitForResponse(&req
);
933 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
936 static void MMDevApiStopPlayback(ALCdevice
*device
)
938 MMDevApiData
*data
= device
->ExtraData
;
939 ThreadRequest req
= { data
->MsgEvent
, 0 };
941 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)device
))
942 (void)WaitForResponse(&req
);
946 static ALint64
MMDevApiGetLatency(ALCdevice
*device
)
948 MMDevApiData
*data
= device
->ExtraData
;
950 return (ALint64
)data
->Padding
* 1000000000 / device
->Frequency
;
954 static const BackendFuncs MMDevApiFuncs
= {
955 MMDevApiOpenPlayback
,
956 MMDevApiClosePlayback
,
957 MMDevApiResetPlayback
,
958 MMDevApiStartPlayback
,
959 MMDevApiStopPlayback
,
966 ALCdevice_LockDefault
,
967 ALCdevice_UnlockDefault
,
972 ALCboolean
alcMMDevApiInit(BackendFuncs
*FuncList
)
976 *FuncList
= MMDevApiFuncs
;
980 void alcMMDevApiDeinit(void)
984 for(i
= 0;i
< NumPlaybackDevices
;i
++)
986 free(PlaybackDeviceList
[i
].name
);
987 free(PlaybackDeviceList
[i
].devid
);
989 free(PlaybackDeviceList
);
990 PlaybackDeviceList
= NULL
;
991 NumPlaybackDevices
= 0;
993 for(i
= 0;i
< NumCaptureDevices
;i
++)
995 free(CaptureDeviceList
[i
].name
);
996 free(CaptureDeviceList
[i
].devid
);
998 free(CaptureDeviceList
);
999 CaptureDeviceList
= NULL
;
1000 NumCaptureDevices
= 0;
1004 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
1005 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
1006 CloseHandle(ThreadHdl
);
1011 void alcMMDevApiProbe(enum DevProbe type
)
1013 ThreadRequest req
= { NULL
, 0 };
1014 HRESULT hr
= E_FAIL
;
1018 case ALL_DEVICE_PROBE
:
1019 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1020 if(req
.FinishedEvt
== NULL
)
1021 ERR("Failed to create event: %lu\n", GetLastError());
1022 else if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
1023 hr
= WaitForResponse(&req
);
1027 for(i
= 0;i
< NumPlaybackDevices
;i
++)
1029 if(PlaybackDeviceList
[i
].name
)
1030 AppendAllDevicesList(PlaybackDeviceList
[i
].name
);
1035 case CAPTURE_DEVICE_PROBE
:
1038 if(req
.FinishedEvt
!= NULL
)
1039 CloseHandle(req
.FinishedEvt
);
1040 req
.FinishedEvt
= NULL
;