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>
32 #ifndef _WAVEFORMATEXTENSIBLE_
42 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
43 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
45 #define MONO SPEAKER_FRONT_CENTER
46 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
47 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
48 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
49 #define X5DOT1SIDE (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
50 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
51 #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)
66 static const ALCchar mmDevice
[] = "WASAPI Default";
69 static HANDLE ThreadHdl
;
70 static DWORD ThreadID
;
77 #define WM_USER_OpenDevice (WM_USER+0)
78 #define WM_USER_ResetDevice (WM_USER+1)
79 #define WM_USER_StopDevice (WM_USER+2)
80 #define WM_USER_CloseDevice (WM_USER+3)
82 static HRESULT
WaitForResponse(ThreadRequest
*req
)
84 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
86 ERR("Message response error: %lu\n", GetLastError());
91 static ALuint
MMDevApiProc(ALvoid
*ptr
)
93 ALCdevice
*device
= ptr
;
94 MMDevApiData
*data
= device
->ExtraData
;
96 IAudioRenderClient
*iface
;
103 hr
= CoInitialize(NULL
);
106 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
107 aluHandleDisconnect(device
);
111 hr
= IAudioClient_GetService(data
->client
, &IID_IAudioRenderClient
, &render
.ptr
);
114 ERR("Failed to get AudioRenderClient service: 0x%08lx\n", hr
);
115 aluHandleDisconnect(device
);
121 while(!data
->killNow
)
123 hr
= IAudioClient_GetCurrentPadding(data
->client
, &written
);
126 ERR("Failed to get padding: 0x%08lx\n", hr
);
127 aluHandleDisconnect(device
);
131 len
= device
->UpdateSize
*device
->NumUpdates
- written
;
132 if(len
< device
->UpdateSize
)
135 res
= WaitForSingleObjectEx(data
->hNotifyEvent
, 2000, FALSE
);
136 if(res
!= WAIT_OBJECT_0
)
137 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
140 len
-= len
%device
->UpdateSize
;
142 hr
= IAudioRenderClient_GetBuffer(render
.iface
, len
, &buffer
);
145 aluMixData(device
, buffer
, len
);
146 hr
= IAudioRenderClient_ReleaseBuffer(render
.iface
, len
, 0);
150 ERR("Failed to buffer data: 0x%08lx\n", hr
);
151 aluHandleDisconnect(device
);
156 IAudioRenderClient_Release(render
.iface
);
163 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
165 memset(out
, 0, sizeof(*out
));
166 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
167 *out
= *(WAVEFORMATEXTENSIBLE
*)in
;
168 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
171 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
172 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
173 if(out
->Format
.nChannels
== 1)
174 out
->dwChannelMask
= MONO
;
175 else if(out
->Format
.nChannels
== 2)
176 out
->dwChannelMask
= STEREO
;
178 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
179 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
181 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
184 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
185 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
186 if(out
->Format
.nChannels
== 1)
187 out
->dwChannelMask
= MONO
;
188 else if(out
->Format
.nChannels
== 2)
189 out
->dwChannelMask
= STEREO
;
191 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
192 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
196 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
202 static HRESULT
DoReset(ALCdevice
*device
)
204 MMDevApiData
*data
= device
->ExtraData
;
205 WAVEFORMATEXTENSIBLE OutputType
;
206 WAVEFORMATEX
*wfx
= NULL
;
207 REFERENCE_TIME min_per
;
208 UINT32 buffer_len
, min_len
;
211 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
214 ERR("Failed to get mix format: 0x%08lx\n", hr
);
218 if(!MakeExtensible(&OutputType
, wfx
))
226 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
227 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
228 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
230 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
231 device
->FmtChans
= DevFmtMono
;
232 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
233 device
->FmtChans
= DevFmtStereo
;
234 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
235 device
->FmtChans
= DevFmtQuad
;
236 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
237 device
->FmtChans
= DevFmtX51
;
238 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
239 device
->FmtChans
= DevFmtX51Side
;
240 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
241 device
->FmtChans
= DevFmtX61
;
242 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
243 device
->FmtChans
= DevFmtX71
;
245 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
248 switch(device
->FmtChans
)
251 OutputType
.Format
.nChannels
= 1;
252 OutputType
.dwChannelMask
= MONO
;
255 OutputType
.Format
.nChannels
= 2;
256 OutputType
.dwChannelMask
= STEREO
;
259 OutputType
.Format
.nChannels
= 4;
260 OutputType
.dwChannelMask
= QUAD
;
263 OutputType
.Format
.nChannels
= 6;
264 OutputType
.dwChannelMask
= X5DOT1
;
267 OutputType
.Format
.nChannels
= 6;
268 OutputType
.dwChannelMask
= X5DOT1SIDE
;
271 OutputType
.Format
.nChannels
= 7;
272 OutputType
.dwChannelMask
= X6DOT1
;
275 OutputType
.Format
.nChannels
= 8;
276 OutputType
.dwChannelMask
= X7DOT1
;
279 switch(device
->FmtType
)
282 device
->FmtType
= DevFmtUByte
;
285 OutputType
.Format
.wBitsPerSample
= 8;
286 OutputType
.Samples
.wValidBitsPerSample
= 8;
287 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
290 device
->FmtType
= DevFmtShort
;
293 OutputType
.Format
.wBitsPerSample
= 16;
294 OutputType
.Samples
.wValidBitsPerSample
= 16;
295 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
298 OutputType
.Format
.wBitsPerSample
= 32;
299 OutputType
.Samples
.wValidBitsPerSample
= 32;
300 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
303 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
305 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
306 OutputType
.Format
.wBitsPerSample
/ 8;
307 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
308 OutputType
.Format
.nBlockAlign
;
310 hr
= IAudioClient_IsFormatSupported(data
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
313 ERR("Failed to check format support: 0x%08lx\n", hr
);
314 hr
= IAudioClient_GetMixFormat(data
->client
, &wfx
);
318 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
324 if(!MakeExtensible(&OutputType
, wfx
))
332 if(device
->Frequency
!= OutputType
.Format
.nSamplesPerSec
)
334 if((device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
335 ERR("Failed to set %dhz, got %ldhz instead\n", device
->Frequency
, OutputType
.Format
.nSamplesPerSec
);
336 device
->Flags
&= ~DEVICE_FREQUENCY_REQUEST
;
337 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
340 if(!((device
->FmtChans
== DevFmtMono
&& OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
) ||
341 (device
->FmtChans
== DevFmtStereo
&& OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
) ||
342 (device
->FmtChans
== DevFmtQuad
&& OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
) ||
343 (device
->FmtChans
== DevFmtX51
&& OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
) ||
344 (device
->FmtChans
== DevFmtX51Side
&& OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
) ||
345 (device
->FmtChans
== DevFmtX61
&& OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
) ||
346 (device
->FmtChans
== DevFmtX71
&& OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)))
348 if((device
->Flags
&DEVICE_CHANNELS_REQUEST
))
349 ERR("Failed to set %s, got %d channels (0x%08lx) instead\n", DevFmtChannelsString(device
->FmtChans
), OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
350 device
->Flags
&= ~DEVICE_CHANNELS_REQUEST
;
352 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
353 device
->FmtChans
= DevFmtMono
;
354 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
355 device
->FmtChans
= DevFmtStereo
;
356 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
357 device
->FmtChans
= DevFmtQuad
;
358 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
359 device
->FmtChans
= DevFmtX51
;
360 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1SIDE
)
361 device
->FmtChans
= DevFmtX51Side
;
362 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
363 device
->FmtChans
= DevFmtX61
;
364 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
365 device
->FmtChans
= DevFmtX71
;
368 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
369 device
->FmtChans
= DevFmtStereo
;
370 OutputType
.Format
.nChannels
= 2;
371 OutputType
.dwChannelMask
= STEREO
;
375 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
377 if(OutputType
.Samples
.wValidBitsPerSample
== 0)
378 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
379 if(OutputType
.Samples
.wValidBitsPerSample
!= OutputType
.Format
.wBitsPerSample
||
380 !((device
->FmtType
== DevFmtUByte
&& OutputType
.Format
.wBitsPerSample
== 8) ||
381 (device
->FmtType
== DevFmtShort
&& OutputType
.Format
.wBitsPerSample
== 16)))
383 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device
->FmtType
), OutputType
.Samples
.wValidBitsPerSample
, OutputType
.Format
.wBitsPerSample
);
384 if(OutputType
.Format
.wBitsPerSample
== 8)
385 device
->FmtType
= DevFmtUByte
;
386 else if(OutputType
.Format
.wBitsPerSample
== 16)
387 device
->FmtType
= DevFmtShort
;
390 device
->FmtType
= DevFmtShort
;
391 OutputType
.Format
.wBitsPerSample
= 16;
393 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
396 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
398 if(OutputType
.Samples
.wValidBitsPerSample
== 0)
399 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
400 if(OutputType
.Samples
.wValidBitsPerSample
!= OutputType
.Format
.wBitsPerSample
||
401 !((device
->FmtType
== DevFmtFloat
&& OutputType
.Format
.wBitsPerSample
== 32)))
403 ERR("Failed to set %s samples, got %d/%d-bit instead\n", DevFmtTypeString(device
->FmtType
), OutputType
.Samples
.wValidBitsPerSample
, OutputType
.Format
.wBitsPerSample
);
404 if(OutputType
.Format
.wBitsPerSample
!= 32)
406 device
->FmtType
= DevFmtFloat
;
407 OutputType
.Format
.wBitsPerSample
= 32;
409 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
414 ERR("Unhandled format sub-type\n");
415 device
->FmtType
= DevFmtShort
;
416 OutputType
.Format
.wBitsPerSample
= 16;
417 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
418 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
422 SetDefaultWFXChannelOrder(device
);
424 hr
= IAudioClient_GetDevicePeriod(data
->client
, &min_per
, NULL
);
427 min_len
= (min_per
*device
->Frequency
+ 10000000-1) / 10000000;
428 if(min_len
< device
->UpdateSize
)
429 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
431 device
->NumUpdates
= (device
->NumUpdates
*device
->UpdateSize
+ min_len
/2) /
433 device
->NumUpdates
= maxu(device
->NumUpdates
, 2);
434 device
->UpdateSize
= min_len
;
436 hr
= IAudioClient_Initialize(data
->client
, AUDCLNT_SHAREMODE_SHARED
,
437 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
438 ((REFERENCE_TIME
)device
->UpdateSize
*
439 device
->NumUpdates
*10000000 +
440 device
->Frequency
-1) / device
->Frequency
,
441 0, &OutputType
.Format
, NULL
);
445 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
449 hr
= IAudioClient_GetBufferSize(data
->client
, &buffer_len
);
452 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
456 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
457 if(device
->NumUpdates
<= 1)
459 device
->NumUpdates
= 1;
460 ERR("Audio client returned buffer_len < period*2; expect break up\n");
463 ResetEvent(data
->hNotifyEvent
);
464 hr
= IAudioClient_SetEventHandle(data
->client
, data
->hNotifyEvent
);
466 hr
= IAudioClient_Start(data
->client
);
469 ERR("Failed to start audio client: 0x%08lx\n", hr
);
473 data
->thread
= StartThread(MMDevApiProc
, device
);
476 IAudioClient_Stop(data
->client
);
477 ERR("Failed to start thread\n");
485 static DWORD CALLBACK
MMDevApiMsgProc(void *ptr
)
487 ThreadRequest
*req
= ptr
;
488 IMMDeviceEnumerator
*Enumerator
;
494 TRACE("Starting message thread\n");
496 hr
= CoInitialize(NULL
);
499 WARN("Failed to initialize COM: 0x%08lx\n", hr
);
501 SetEvent(req
->FinishedEvt
);
505 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
508 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
511 SetEvent(req
->FinishedEvt
);
515 IMMDeviceEnumerator_Release(Enumerator
);
519 SetEvent(req
->FinishedEvt
);
521 TRACE("Starting message loop\n");
522 while(GetMessage(&msg
, NULL
, 0, 0))
524 TRACE("Got message %u\n", msg
.message
);
527 case WM_USER_OpenDevice
:
528 req
= (ThreadRequest
*)msg
.wParam
;
529 device
= (ALCdevice
*)msg
.lParam
;
530 data
= device
->ExtraData
;
532 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
536 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &data
->mmdev
);
537 IMMDeviceEnumerator_Release(Enumerator
);
541 hr
= IMMDevice_Activate(data
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
548 IMMDevice_Release(data
->mmdev
);
553 SetEvent(req
->FinishedEvt
);
556 case WM_USER_ResetDevice
:
557 req
= (ThreadRequest
*)msg
.wParam
;
558 device
= (ALCdevice
*)msg
.lParam
;
560 req
->result
= DoReset(device
);
561 SetEvent(req
->FinishedEvt
);
564 case WM_USER_StopDevice
:
565 req
= (ThreadRequest
*)msg
.wParam
;
566 device
= (ALCdevice
*)msg
.lParam
;
567 data
= device
->ExtraData
;
572 StopThread(data
->thread
);
577 IAudioClient_Stop(data
->client
);
581 SetEvent(req
->FinishedEvt
);
584 case WM_USER_CloseDevice
:
585 req
= (ThreadRequest
*)msg
.wParam
;
586 device
= (ALCdevice
*)msg
.lParam
;
587 data
= device
->ExtraData
;
589 IAudioClient_Release(data
->client
);
592 IMMDevice_Release(data
->mmdev
);
596 SetEvent(req
->FinishedEvt
);
600 ERR("Unexpected message: %u\n", msg
.message
);
604 TRACE("Message loop finished\n");
611 static BOOL
MMDevApiLoad(void)
613 static HRESULT InitResult
;
619 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
620 if(req
.FinishedEvt
== NULL
)
621 ERR("Failed to create event: %lu\n", GetLastError());
624 ThreadHdl
= CreateThread(NULL
, 0, MMDevApiMsgProc
, &req
, 0, &ThreadID
);
625 if(ThreadHdl
!= NULL
)
626 InitResult
= WaitForResponse(&req
);
627 CloseHandle(req
.FinishedEvt
);
630 return SUCCEEDED(InitResult
);
634 static ALCenum
MMDevApiOpenPlayback(ALCdevice
*device
, const ALCchar
*deviceName
)
636 MMDevApiData
*data
= NULL
;
640 deviceName
= mmDevice
;
641 else if(strcmp(deviceName
, mmDevice
) != 0)
642 return ALC_INVALID_VALUE
;
644 //Initialise requested device
645 data
= calloc(1, sizeof(MMDevApiData
));
647 return ALC_OUT_OF_MEMORY
;
648 device
->ExtraData
= data
;
651 data
->hNotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
652 data
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
653 if(data
->hNotifyEvent
== NULL
|| data
->MsgEvent
== NULL
)
658 ThreadRequest req
= { data
->MsgEvent
, 0 };
661 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)device
))
662 hr
= WaitForResponse(&req
);
667 if(data
->hNotifyEvent
!= NULL
)
668 CloseHandle(data
->hNotifyEvent
);
669 data
->hNotifyEvent
= NULL
;
670 if(data
->MsgEvent
!= NULL
)
671 CloseHandle(data
->MsgEvent
);
672 data
->MsgEvent
= NULL
;
675 device
->ExtraData
= NULL
;
677 ERR("Device init failed: 0x%08lx\n", hr
);
678 return ALC_INVALID_VALUE
;
681 device
->szDeviceName
= strdup(deviceName
);
685 static void MMDevApiClosePlayback(ALCdevice
*device
)
687 MMDevApiData
*data
= device
->ExtraData
;
688 ThreadRequest req
= { data
->MsgEvent
, 0 };
690 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)device
))
691 (void)WaitForResponse(&req
);
693 CloseHandle(data
->MsgEvent
);
694 data
->MsgEvent
= NULL
;
696 CloseHandle(data
->hNotifyEvent
);
697 data
->hNotifyEvent
= NULL
;
700 device
->ExtraData
= NULL
;
703 static ALCboolean
MMDevApiResetPlayback(ALCdevice
*device
)
705 MMDevApiData
*data
= device
->ExtraData
;
706 ThreadRequest req
= { data
->MsgEvent
, 0 };
709 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)device
))
710 hr
= WaitForResponse(&req
);
712 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
715 static void MMDevApiStopPlayback(ALCdevice
*device
)
717 MMDevApiData
*data
= device
->ExtraData
;
718 ThreadRequest req
= { data
->MsgEvent
, 0 };
720 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)device
))
721 (void)WaitForResponse(&req
);
725 static const BackendFuncs MMDevApiFuncs
= {
726 MMDevApiOpenPlayback
,
727 MMDevApiClosePlayback
,
728 MMDevApiResetPlayback
,
729 MMDevApiStopPlayback
,
739 ALCboolean
alcMMDevApiInit(BackendFuncs
*FuncList
)
743 *FuncList
= MMDevApiFuncs
;
747 void alcMMDevApiDeinit(void)
751 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
752 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
753 CloseHandle(ThreadHdl
);
758 void alcMMDevApiProbe(enum DevProbe type
)
763 AppendDeviceList(mmDevice
);
765 case ALL_DEVICE_PROBE
:
766 AppendAllDeviceList(mmDevice
);
768 case CAPTURE_DEVICE_PROBE
: