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)
62 IAudioRenderClient
*render
;
77 static DevMap
*PlaybackDeviceList
;
78 static ALuint NumPlaybackDevices
;
79 static DevMap
*CaptureDeviceList
;
80 static ALuint NumCaptureDevices
;
83 static HANDLE ThreadHdl
;
84 static DWORD ThreadID
;
91 #define WM_USER_OpenDevice (WM_USER+0)
92 #define WM_USER_ResetDevice (WM_USER+1)
93 #define WM_USER_StartDevice (WM_USER+2)
94 #define WM_USER_StopDevice (WM_USER+3)
95 #define WM_USER_CloseDevice (WM_USER+4)
96 #define WM_USER_Enumerate (WM_USER+5)
98 static HRESULT
WaitForResponse(ThreadRequest
*req
)
100 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
102 ERR("Message response error: %lu\n", GetLastError());
107 static ALCchar
*get_device_name(IMMDevice
*device
)
109 ALCchar
*name
= NULL
;
115 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
118 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
122 PropVariantInit(&pvname
);
124 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
127 WARN("GetValue failed: 0x%08lx\n", hr
);
132 if((len
=WideCharToMultiByte(CP_ACP
, 0, pvname
.pwszVal
, -1, NULL
, 0, NULL
, NULL
)) > 0)
134 name
= calloc(1, len
);
135 WideCharToMultiByte(CP_ACP
, 0, pvname
.pwszVal
, -1, name
, len
, NULL
, NULL
);
139 PropVariantClear(&pvname
);
140 IPropertyStore_Release(ps
);
145 static void add_device(IMMDevice
*device
, DevMap
*devmap
)
150 hr
= IMMDevice_GetId(device
, &devid
);
153 devmap
->devid
= strdupW(devid
);
154 devmap
->name
= get_device_name(device
);
155 TRACE("Got device \"%s\", \"%ls\"\n", devmap
->name
, devmap
->devid
);
156 CoTaskMemFree(devid
);
160 static DevMap
*ProbeDevices(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, ALuint
*numdevs
)
162 IMMDeviceCollection
*coll
;
163 IMMDevice
*defdev
= NULL
;
164 DevMap
*devlist
= NULL
;
170 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
173 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
178 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
179 if(SUCCEEDED(hr
) && count
> 0)
181 devlist
= calloc(count
, sizeof(*devlist
));
184 IMMDeviceCollection_Release(coll
);
188 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, flowdir
,
189 eMultimedia
, &defdev
);
191 if(SUCCEEDED(hr
) && defdev
!= NULL
)
192 add_device(defdev
, &devlist
[idx
++]);
194 for(i
= 0;i
< count
&& idx
< count
;++i
)
198 if(FAILED(IMMDeviceCollection_Item(coll
, i
, &device
)))
202 add_device(device
, &devlist
[idx
++]);
204 IMMDevice_Release(device
);
207 if(defdev
) IMMDevice_Release(defdev
);
208 IMMDeviceCollection_Release(coll
);
215 static ALuint
MMDevApiProc(ALvoid
*ptr
)
217 ALCdevice
*device
= ptr
;
218 MMDevApiData
*data
= device
->ExtraData
;
219 UINT32 update_size
, num_updates
;
224 hr
= CoInitialize(NULL
);
227 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
228 aluHandleDisconnect(device
);
234 update_size
= device
->UpdateSize
;
235 num_updates
= device
->NumUpdates
;
236 while(!data
->killNow
)
238 hr
= IAudioClient_GetCurrentPadding(data
->client
, &written
);
241 ERR("Failed to get padding: 0x%08lx\n", hr
);
242 aluHandleDisconnect(device
);
246 len
= update_size
*num_updates
- written
;
247 if(len
< update_size
)
250 res
= WaitForSingleObjectEx(data
->hNotifyEvent
, 2000, FALSE
);
251 if(res
!= WAIT_OBJECT_0
)
252 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
255 len
-= len
%update_size
;
257 hr
= IAudioRenderClient_GetBuffer(data
->render
, len
, &buffer
);
260 aluMixData(device
, buffer
, len
);
261 hr
= IAudioRenderClient_ReleaseBuffer(data
->render
, len
, 0);
265 ERR("Failed to buffer data: 0x%08lx\n", hr
);
266 aluHandleDisconnect(device
);
276 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
278 memset(out
, 0, sizeof(*out
));
279 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
280 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
281 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
284 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
285 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
286 if(out
->Format
.nChannels
== 1)
287 out
->dwChannelMask
= MONO
;
288 else if(out
->Format
.nChannels
== 2)
289 out
->dwChannelMask
= STEREO
;
291 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
292 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
294 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
297 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
298 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
299 if(out
->Format
.nChannels
== 1)
300 out
->dwChannelMask
= MONO
;
301 else if(out
->Format
.nChannels
== 2)
302 out
->dwChannelMask
= STEREO
;
304 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
305 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
309 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
315 static HRESULT
DoReset(ALCdevice
*device
)
317 MMDevApiData
*data
= device
->ExtraData
;
318 WAVEFORMATEXTENSIBLE OutputType
;
319 WAVEFORMATEX
*wfx
= NULL
;
320 REFERENCE_TIME min_per
, buf_time
;
321 UINT32 buffer_len
, min_len
;
324 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
327 ERR("Failed to get mix format: 0x%08lx\n", hr
);
331 if(!MakeExtensible(&OutputType
, wfx
))
339 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
340 device
->Frequency
-1) / device
->Frequency
;
342 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
343 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
344 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
346 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
347 device
->FmtChans
= DevFmtMono
;
348 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
349 device
->FmtChans
= DevFmtStereo
;
350 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
351 device
->FmtChans
= DevFmtQuad
;
352 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
353 device
->FmtChans
= DevFmtX51
;
354 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
355 device
->FmtChans
= DevFmtX51Side
;
356 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
357 device
->FmtChans
= DevFmtX61
;
358 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
359 device
->FmtChans
= DevFmtX71
;
361 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
364 switch(device
->FmtChans
)
367 OutputType
.Format
.nChannels
= 1;
368 OutputType
.dwChannelMask
= MONO
;
371 OutputType
.Format
.nChannels
= 2;
372 OutputType
.dwChannelMask
= STEREO
;
375 OutputType
.Format
.nChannels
= 4;
376 OutputType
.dwChannelMask
= QUAD
;
379 OutputType
.Format
.nChannels
= 6;
380 OutputType
.dwChannelMask
= X5DOT1
;
383 OutputType
.Format
.nChannels
= 6;
384 OutputType
.dwChannelMask
= X5DOT1SIDE
;
387 OutputType
.Format
.nChannels
= 7;
388 OutputType
.dwChannelMask
= X6DOT1
;
391 OutputType
.Format
.nChannels
= 8;
392 OutputType
.dwChannelMask
= X7DOT1
;
395 switch(device
->FmtType
)
398 device
->FmtType
= DevFmtUByte
;
401 OutputType
.Format
.wBitsPerSample
= 8;
402 OutputType
.Samples
.wValidBitsPerSample
= 8;
403 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
406 device
->FmtType
= DevFmtShort
;
409 OutputType
.Format
.wBitsPerSample
= 16;
410 OutputType
.Samples
.wValidBitsPerSample
= 16;
411 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
414 device
->FmtType
= DevFmtInt
;
417 OutputType
.Format
.wBitsPerSample
= 32;
418 OutputType
.Samples
.wValidBitsPerSample
= 32;
419 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
422 OutputType
.Format
.wBitsPerSample
= 32;
423 OutputType
.Samples
.wValidBitsPerSample
= 32;
424 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
427 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
429 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
430 OutputType
.Format
.wBitsPerSample
/ 8;
431 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
432 OutputType
.Format
.nBlockAlign
;
434 hr
= IAudioClient_IsFormatSupported(data
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
437 ERR("Failed to check format support: 0x%08lx\n", hr
);
438 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
442 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
448 if(!MakeExtensible(&OutputType
, wfx
))
456 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
457 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
458 device
->FmtChans
= DevFmtMono
;
459 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
460 device
->FmtChans
= DevFmtStereo
;
461 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
462 device
->FmtChans
= DevFmtQuad
;
463 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
464 device
->FmtChans
= DevFmtX51
;
465 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
466 device
->FmtChans
= DevFmtX51Side
;
467 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
468 device
->FmtChans
= DevFmtX61
;
469 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
470 device
->FmtChans
= DevFmtX71
;
473 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
474 device
->FmtChans
= DevFmtStereo
;
475 OutputType
.Format
.nChannels
= 2;
476 OutputType
.dwChannelMask
= STEREO
;
479 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
481 if(OutputType
.Format
.wBitsPerSample
== 8)
482 device
->FmtType
= DevFmtUByte
;
483 else if(OutputType
.Format
.wBitsPerSample
== 16)
484 device
->FmtType
= DevFmtShort
;
485 else if(OutputType
.Format
.wBitsPerSample
== 32)
486 device
->FmtType
= DevFmtInt
;
489 device
->FmtType
= DevFmtShort
;
490 OutputType
.Format
.wBitsPerSample
= 16;
493 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
495 device
->FmtType
= DevFmtFloat
;
496 OutputType
.Format
.wBitsPerSample
= 32;
500 ERR("Unhandled format sub-type\n");
501 device
->FmtType
= DevFmtShort
;
502 OutputType
.Format
.wBitsPerSample
= 16;
503 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
505 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
508 SetDefaultWFXChannelOrder(device
);
510 hr
= IAudioClient_Initialize(data
->client
, AUDCLNT_SHAREMODE_SHARED
,
511 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
512 buf_time
, 0, &OutputType
.Format
, NULL
);
515 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
519 hr
= IAudioClient_GetDevicePeriod(data
->client
, &min_per
, NULL
);
522 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
523 /* Find the nearest multiple of the period size to the update size */
524 if(min_len
< device
->UpdateSize
)
525 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
526 hr
= IAudioClient_GetBufferSize(data
->client
, &buffer_len
);
530 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
534 device
->UpdateSize
= min_len
;
535 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
536 if(device
->NumUpdates
<= 1)
538 ERR("Audio client returned buffer_len < period*2; expect break up\n");
539 device
->NumUpdates
= 2;
540 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
547 static DWORD CALLBACK
MMDevApiMsgProc(void *ptr
)
549 ThreadRequest
*req
= ptr
;
550 IMMDeviceEnumerator
*Enumerator
;
551 ALuint deviceCount
= 0;
557 TRACE("Starting message thread\n");
559 cohr
= CoInitialize(NULL
);
562 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
564 SetEvent(req
->FinishedEvt
);
568 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
571 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
574 SetEvent(req
->FinishedEvt
);
578 IMMDeviceEnumerator_Release(Enumerator
);
584 SetEvent(req
->FinishedEvt
);
586 TRACE("Starting message loop\n");
587 while(GetMessage(&msg
, NULL
, 0, 0))
589 TRACE("Got message %u\n", msg
.message
);
592 case WM_USER_OpenDevice
:
593 req
= (ThreadRequest
*)msg
.wParam
;
594 device
= (ALCdevice
*)msg
.lParam
;
595 data
= device
->ExtraData
;
598 if(++deviceCount
== 1)
599 hr
= cohr
= CoInitialize(NULL
);
601 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
606 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &data
->mmdev
);
608 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, data
->devid
, &data
->mmdev
);
609 IMMDeviceEnumerator_Release(Enumerator
);
613 hr
= IMMDevice_Activate(data
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
617 device
->szDeviceName
= get_device_name(data
->mmdev
);
623 IMMDevice_Release(data
->mmdev
);
625 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
630 SetEvent(req
->FinishedEvt
);
633 case WM_USER_ResetDevice
:
634 req
= (ThreadRequest
*)msg
.wParam
;
635 device
= (ALCdevice
*)msg
.lParam
;
637 req
->result
= DoReset(device
);
638 SetEvent(req
->FinishedEvt
);
641 case WM_USER_StartDevice
:
642 req
= (ThreadRequest
*)msg
.wParam
;
643 device
= (ALCdevice
*)msg
.lParam
;
644 data
= device
->ExtraData
;
646 ResetEvent(data
->hNotifyEvent
);
647 hr
= IAudioClient_SetEventHandle(data
->client
, data
->hNotifyEvent
);
649 ERR("Failed to set event handle: 0x%08lx\n", hr
);
652 hr
= IAudioClient_Start(data
->client
);
654 ERR("Failed to start audio client: 0x%08lx\n", hr
);
658 hr
= IAudioClient_GetService(data
->client
, &IID_IAudioRenderClient
, &ptr
);
662 data
->thread
= StartThread(MMDevApiProc
, device
);
666 IAudioRenderClient_Release(data
->render
);
668 IAudioClient_Stop(data
->client
);
669 ERR("Failed to start thread\n");
675 SetEvent(req
->FinishedEvt
);
678 case WM_USER_StopDevice
:
679 req
= (ThreadRequest
*)msg
.wParam
;
680 device
= (ALCdevice
*)msg
.lParam
;
681 data
= device
->ExtraData
;
686 StopThread(data
->thread
);
691 IAudioRenderClient_Release(data
->render
);
693 IAudioClient_Stop(data
->client
);
697 SetEvent(req
->FinishedEvt
);
700 case WM_USER_CloseDevice
:
701 req
= (ThreadRequest
*)msg
.wParam
;
702 device
= (ALCdevice
*)msg
.lParam
;
703 data
= device
->ExtraData
;
705 IAudioClient_Release(data
->client
);
708 IMMDevice_Release(data
->mmdev
);
711 if(--deviceCount
== 0)
715 SetEvent(req
->FinishedEvt
);
718 case WM_USER_Enumerate
:
719 req
= (ThreadRequest
*)msg
.wParam
;
722 if(++deviceCount
== 1)
723 hr
= cohr
= CoInitialize(NULL
);
725 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
734 if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
737 devlist
= &CaptureDeviceList
;
738 numdevs
= &NumCaptureDevices
;
743 devlist
= &PlaybackDeviceList
;
744 numdevs
= &NumPlaybackDevices
;
747 for(i
= 0;i
< *numdevs
;i
++)
749 free((*devlist
)[i
].name
);
750 free((*devlist
)[i
].devid
);
756 *devlist
= ProbeDevices(Enumerator
, flowdir
, numdevs
);
758 IMMDeviceEnumerator_Release(Enumerator
);
762 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
766 SetEvent(req
->FinishedEvt
);
770 ERR("Unexpected message: %u\n", msg
.message
);
774 TRACE("Message loop finished\n");
780 static BOOL
MMDevApiLoad(void)
782 static HRESULT InitResult
;
788 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
789 if(req
.FinishedEvt
== NULL
)
790 ERR("Failed to create event: %lu\n", GetLastError());
793 ThreadHdl
= CreateThread(NULL
, 0, MMDevApiMsgProc
, &req
, 0, &ThreadID
);
794 if(ThreadHdl
!= NULL
)
795 InitResult
= WaitForResponse(&req
);
796 CloseHandle(req
.FinishedEvt
);
799 return SUCCEEDED(InitResult
);
803 static ALCenum
MMDevApiOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
805 MMDevApiData
*data
= NULL
;
808 //Initialise requested device
809 data
= calloc(1, sizeof(MMDevApiData
));
811 return ALC_OUT_OF_MEMORY
;
812 device
->ExtraData
= data
;
815 data
->hNotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
816 data
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
817 if(data
->hNotifyEvent
== NULL
|| data
->MsgEvent
== NULL
)
826 if(!PlaybackDeviceList
)
828 ThreadRequest req
= { data
->MsgEvent
, 0 };
829 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
830 (void)WaitForResponse(&req
);
834 for(i
= 0;i
< NumPlaybackDevices
;i
++)
836 if(strcmp(deviceName
, PlaybackDeviceList
[i
].name
) == 0)
838 data
->devid
= strdupW(PlaybackDeviceList
[i
].devid
);
848 ThreadRequest req
= { data
->MsgEvent
, 0 };
851 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)device
))
852 hr
= WaitForResponse(&req
);
857 if(data
->hNotifyEvent
!= NULL
)
858 CloseHandle(data
->hNotifyEvent
);
859 data
->hNotifyEvent
= NULL
;
860 if(data
->MsgEvent
!= NULL
)
861 CloseHandle(data
->MsgEvent
);
862 data
->MsgEvent
= NULL
;
865 device
->ExtraData
= NULL
;
867 ERR("Device init failed: 0x%08lx\n", hr
);
868 return ALC_INVALID_VALUE
;
874 static void MMDevApiClosePlayback(ALCdevice
*device
)
876 MMDevApiData
*data
= device
->ExtraData
;
877 ThreadRequest req
= { data
->MsgEvent
, 0 };
879 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)device
))
880 (void)WaitForResponse(&req
);
882 CloseHandle(data
->MsgEvent
);
883 data
->MsgEvent
= NULL
;
885 CloseHandle(data
->hNotifyEvent
);
886 data
->hNotifyEvent
= NULL
;
892 device
->ExtraData
= NULL
;
895 static ALCboolean
MMDevApiResetPlayback(ALCdevice
*device
)
897 MMDevApiData
*data
= device
->ExtraData
;
898 ThreadRequest req
= { data
->MsgEvent
, 0 };
901 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)device
))
902 hr
= WaitForResponse(&req
);
904 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
907 static ALCboolean
MMDevApiStartPlayback(ALCdevice
*device
)
909 MMDevApiData
*data
= device
->ExtraData
;
910 ThreadRequest req
= { data
->MsgEvent
, 0 };
913 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)device
))
914 hr
= WaitForResponse(&req
);
916 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
919 static void MMDevApiStopPlayback(ALCdevice
*device
)
921 MMDevApiData
*data
= device
->ExtraData
;
922 ThreadRequest req
= { data
->MsgEvent
, 0 };
924 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)device
))
925 (void)WaitForResponse(&req
);
929 static const BackendFuncs MMDevApiFuncs
= {
930 MMDevApiOpenPlayback
,
931 MMDevApiClosePlayback
,
932 MMDevApiResetPlayback
,
933 MMDevApiStartPlayback
,
934 MMDevApiStopPlayback
,
944 ALCboolean
alcMMDevApiInit(BackendFuncs
*FuncList
)
948 *FuncList
= MMDevApiFuncs
;
952 void alcMMDevApiDeinit(void)
956 for(i
= 0;i
< NumPlaybackDevices
;i
++)
958 free(PlaybackDeviceList
[i
].name
);
959 free(PlaybackDeviceList
[i
].devid
);
961 free(PlaybackDeviceList
);
962 PlaybackDeviceList
= NULL
;
963 NumPlaybackDevices
= 0;
965 for(i
= 0;i
< NumCaptureDevices
;i
++)
967 free(CaptureDeviceList
[i
].name
);
968 free(CaptureDeviceList
[i
].devid
);
970 free(CaptureDeviceList
);
971 CaptureDeviceList
= NULL
;
972 NumCaptureDevices
= 0;
976 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
977 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
978 CloseHandle(ThreadHdl
);
983 void alcMMDevApiProbe(enum DevProbe type
)
985 ThreadRequest req
= { NULL
, 0 };
990 case ALL_DEVICE_PROBE
:
991 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
992 if(req
.FinishedEvt
== NULL
)
993 ERR("Failed to create event: %lu\n", GetLastError());
994 else if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
995 hr
= WaitForResponse(&req
);
999 for(i
= 0;i
< NumPlaybackDevices
;i
++)
1001 if(PlaybackDeviceList
[i
].name
)
1002 AppendAllDeviceList(PlaybackDeviceList
[i
].name
);
1007 case CAPTURE_DEVICE_PROBE
:
1010 if(req
.FinishedEvt
!= NULL
)
1011 CloseHandle(req
.FinishedEvt
);
1012 req
.FinishedEvt
= NULL
;