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.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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_
47 #include "backends/base.h"
50 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
51 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
53 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName
, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
54 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor
, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
56 #define MONO SPEAKER_FRONT_CENTER
57 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
58 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
59 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
60 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
61 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
62 #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)
63 #define X7DOT1_NARROW (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_FRONT_LEFT_OF_CENTER|SPEAKER_FRONT_RIGHT_OF_CENTER)
70 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
72 static void clear_devlist(vector_DevMap
*list
)
74 #define CLEAR_DEVMAP(i) do { \
75 AL_STRING_DEINIT((i)->name); \
79 VECTOR_FOR_EACH(DevMap
, *list
, CLEAR_DEVMAP
);
80 VECTOR_RESIZE(*list
, 0);
84 static vector_DevMap PlaybackDevices
;
85 static vector_DevMap CaptureDevices
;
88 static HANDLE ThreadHdl
;
89 static DWORD ThreadID
;
96 #define WM_USER_First (WM_USER+0)
97 #define WM_USER_OpenDevice (WM_USER+0)
98 #define WM_USER_ResetDevice (WM_USER+1)
99 #define WM_USER_StartDevice (WM_USER+2)
100 #define WM_USER_StopDevice (WM_USER+3)
101 #define WM_USER_CloseDevice (WM_USER+4)
102 #define WM_USER_Enumerate (WM_USER+5)
103 #define WM_USER_Last (WM_USER+5)
105 static inline void ReturnMsgResponse(ThreadRequest
*req
, HRESULT res
)
108 SetEvent(req
->FinishedEvt
);
111 static HRESULT
WaitForResponse(ThreadRequest
*req
)
113 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
115 ERR("Message response error: %lu\n", GetLastError());
120 static void get_device_name(IMMDevice
*device
, al_string
*name
)
126 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
129 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
133 PropVariantInit(&pvname
);
135 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
137 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr
);
138 else if(pvname
.vt
== VT_LPWSTR
)
139 al_string_copy_wcstr(name
, pvname
.pwszVal
);
141 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname
.vt
);
143 PropVariantClear(&pvname
);
144 IPropertyStore_Release(ps
);
147 static void get_device_formfactor(IMMDevice
*device
, EndpointFormFactor
*formfactor
)
153 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
156 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
160 PropVariantInit(&pvform
);
162 hr
= IPropertyStore_GetValue(ps
, &PKEY_AudioEndpoint_FormFactor
, &pvform
);
164 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr
);
165 else if(pvform
.vt
== VT_UI4
)
166 *formfactor
= pvform
.ulVal
;
167 else if(pvform
.vt
== VT_EMPTY
)
168 *formfactor
= UnknownFormFactor
;
170 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform
.vt
);
172 PropVariantClear(&pvform
);
173 IPropertyStore_Release(ps
);
177 static void add_device(IMMDevice
*device
, LPCWSTR devid
, vector_DevMap
*list
)
181 AL_STRING_INIT(entry
.name
);
182 entry
.devid
= strdupW(devid
);
183 get_device_name(device
, &entry
.name
);
185 TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry
.name
), entry
.devid
);
186 VECTOR_PUSH_BACK(*list
, entry
);
189 static LPWSTR
get_device_id(IMMDevice
*device
)
194 hr
= IMMDevice_GetId(device
, &devid
);
197 ERR("Failed to get device id: %lx\n", hr
);
204 static HRESULT
probe_devices(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, vector_DevMap
*list
)
206 IMMDeviceCollection
*coll
;
207 IMMDevice
*defdev
= NULL
;
208 LPWSTR defdevid
= NULL
;
213 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
216 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
221 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
222 if(SUCCEEDED(hr
) && count
> 0)
225 if(!VECTOR_RESERVE(*list
, count
))
227 IMMDeviceCollection_Release(coll
);
228 return E_OUTOFMEMORY
;
231 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, flowdir
,
232 eMultimedia
, &defdev
);
234 if(SUCCEEDED(hr
) && defdev
!= NULL
)
236 defdevid
= get_device_id(defdev
);
238 add_device(defdev
, defdevid
, list
);
241 for(i
= 0;i
< count
;++i
)
246 hr
= IMMDeviceCollection_Item(coll
, i
, &device
);
247 if(FAILED(hr
)) continue;
249 devid
= get_device_id(device
);
252 if(wcscmp(devid
, defdevid
) != 0)
253 add_device(device
, devid
, list
);
254 CoTaskMemFree(devid
);
256 IMMDevice_Release(device
);
259 if(defdev
) IMMDevice_Release(defdev
);
260 if(defdevid
) CoTaskMemFree(defdevid
);
261 IMMDeviceCollection_Release(coll
);
267 /* Proxy interface used by the message handler. */
268 struct ALCmmdevProxyVtable
;
270 typedef struct ALCmmdevProxy
{
271 const struct ALCmmdevProxyVtable
*vtbl
;
274 struct ALCmmdevProxyVtable
{
275 HRESULT (*const openProxy
)(ALCmmdevProxy
*);
276 void (*const closeProxy
)(ALCmmdevProxy
*);
278 HRESULT (*const resetProxy
)(ALCmmdevProxy
*);
279 HRESULT (*const startProxy
)(ALCmmdevProxy
*);
280 void (*const stopProxy
)(ALCmmdevProxy
*);
283 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
284 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
285 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
286 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
287 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
288 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
290 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
291 T##_ALCmmdevProxy_openProxy, \
292 T##_ALCmmdevProxy_closeProxy, \
293 T##_ALCmmdevProxy_resetProxy, \
294 T##_ALCmmdevProxy_startProxy, \
295 T##_ALCmmdevProxy_stopProxy, \
298 static void ALCmmdevProxy_Construct(ALCmmdevProxy
* UNUSED(self
)) { }
299 static void ALCmmdevProxy_Destruct(ALCmmdevProxy
* UNUSED(self
)) { }
301 static DWORD CALLBACK
ALCmmdevProxy_messageHandler(void *ptr
)
303 ThreadRequest
*req
= ptr
;
304 IMMDeviceEnumerator
*Enumerator
;
305 ALuint deviceCount
= 0;
306 ALCmmdevProxy
*proxy
;
310 TRACE("Starting message thread\n");
312 cohr
= CoInitialize(NULL
);
315 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
316 ReturnMsgResponse(req
, cohr
);
320 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
323 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
325 ReturnMsgResponse(req
, hr
);
329 IMMDeviceEnumerator_Release(Enumerator
);
334 /* HACK: Force Windows to create a message queue for this thread before
335 * returning success, otherwise PostThreadMessage may fail if it gets
336 * called before GetMessage.
338 PeekMessage(&msg
, NULL
, WM_USER
, WM_USER
, PM_NOREMOVE
);
340 TRACE("Message thread initialization complete\n");
341 ReturnMsgResponse(req
, S_OK
);
343 TRACE("Starting message loop\n");
344 while(GetMessage(&msg
, NULL
, WM_USER_First
, WM_USER_Last
))
346 TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg
.message
, (void*)msg
.lParam
, (void*)msg
.wParam
);
349 case WM_USER_OpenDevice
:
350 req
= (ThreadRequest
*)msg
.wParam
;
351 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
354 if(++deviceCount
== 1)
355 hr
= cohr
= CoInitialize(NULL
);
357 hr
= V0(proxy
,openProxy
)();
360 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
364 ReturnMsgResponse(req
, hr
);
367 case WM_USER_ResetDevice
:
368 req
= (ThreadRequest
*)msg
.wParam
;
369 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
371 hr
= V0(proxy
,resetProxy
)();
372 ReturnMsgResponse(req
, hr
);
375 case WM_USER_StartDevice
:
376 req
= (ThreadRequest
*)msg
.wParam
;
377 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
379 hr
= V0(proxy
,startProxy
)();
380 ReturnMsgResponse(req
, hr
);
383 case WM_USER_StopDevice
:
384 req
= (ThreadRequest
*)msg
.wParam
;
385 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
387 V0(proxy
,stopProxy
)();
388 ReturnMsgResponse(req
, S_OK
);
391 case WM_USER_CloseDevice
:
392 req
= (ThreadRequest
*)msg
.wParam
;
393 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
395 V0(proxy
,closeProxy
)();
396 if(--deviceCount
== 0)
399 ReturnMsgResponse(req
, S_OK
);
402 case WM_USER_Enumerate
:
403 req
= (ThreadRequest
*)msg
.wParam
;
406 if(++deviceCount
== 1)
407 hr
= cohr
= CoInitialize(NULL
);
409 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
414 if(msg
.lParam
== ALL_DEVICE_PROBE
)
415 hr
= probe_devices(Enumerator
, eRender
, &PlaybackDevices
);
416 else if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
417 hr
= probe_devices(Enumerator
, eCapture
, &CaptureDevices
);
419 IMMDeviceEnumerator_Release(Enumerator
);
423 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
426 ReturnMsgResponse(req
, hr
);
430 ERR("Unexpected message: %u\n", msg
.message
);
434 TRACE("Message loop finished\n");
440 typedef struct ALCmmdevPlayback
{
441 DERIVE_FROM_TYPE(ALCbackend
);
442 DERIVE_FROM_TYPE(ALCmmdevProxy
);
447 IAudioClient
*client
;
448 IAudioRenderClient
*render
;
453 volatile UINT32 Padding
;
455 volatile int killNow
;
459 static int ALCmmdevPlayback_mixerProc(void *arg
);
461 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
);
462 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
);
463 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*name
);
464 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
);
465 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
);
466 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
);
467 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
);
468 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
);
469 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
);
470 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
);
471 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
);
472 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
);
473 static DECLARE_FORWARD2(ALCmmdevPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
474 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, ALCuint
, availableSamples
)
475 static ALint64
ALCmmdevPlayback_getLatency(ALCmmdevPlayback
*self
);
476 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, lock
)
477 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, unlock
)
478 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback
)
480 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback
);
481 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback
);
484 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
)
486 SET_VTABLE2(ALCmmdevPlayback
, ALCbackend
, self
);
487 SET_VTABLE2(ALCmmdevPlayback
, ALCmmdevProxy
, self
);
488 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
489 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
496 self
->NotifyEvent
= NULL
;
498 self
->MsgEvent
= NULL
;
505 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
)
507 if(self
->NotifyEvent
!= NULL
)
508 CloseHandle(self
->NotifyEvent
);
509 self
->NotifyEvent
= NULL
;
510 if(self
->MsgEvent
!= NULL
)
511 CloseHandle(self
->MsgEvent
);
512 self
->MsgEvent
= NULL
;
517 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
518 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
522 FORCE_ALIGN
static int ALCmmdevPlayback_mixerProc(void *arg
)
524 ALCmmdevPlayback
*self
= arg
;
525 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
526 UINT32 buffer_len
, written
;
527 ALuint update_size
, len
;
531 hr
= CoInitialize(NULL
);
534 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
535 V0(device
->Backend
,lock
)();
536 aluHandleDisconnect(device
);
537 V0(device
->Backend
,unlock
)();
542 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
544 update_size
= device
->UpdateSize
;
545 buffer_len
= update_size
* device
->NumUpdates
;
546 while(!self
->killNow
)
548 hr
= IAudioClient_GetCurrentPadding(self
->client
, &written
);
551 ERR("Failed to get padding: 0x%08lx\n", hr
);
552 V0(device
->Backend
,lock
)();
553 aluHandleDisconnect(device
);
554 V0(device
->Backend
,unlock
)();
557 self
->Padding
= written
;
559 len
= buffer_len
- written
;
560 if(len
< update_size
)
563 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
564 if(res
!= WAIT_OBJECT_0
)
565 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
568 len
-= len
%update_size
;
570 hr
= IAudioRenderClient_GetBuffer(self
->render
, len
, &buffer
);
573 V0(device
->Backend
,lock
)();
574 aluMixData(device
, buffer
, len
);
575 self
->Padding
= written
+ len
;
576 V0(device
->Backend
,unlock
)();
577 hr
= IAudioRenderClient_ReleaseBuffer(self
->render
, len
, 0);
581 ERR("Failed to buffer data: 0x%08lx\n", hr
);
582 V0(device
->Backend
,lock
)();
583 aluHandleDisconnect(device
);
584 V0(device
->Backend
,unlock
)();
595 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
597 memset(out
, 0, sizeof(*out
));
598 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
599 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
600 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
603 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
604 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
605 if(out
->Format
.nChannels
== 1)
606 out
->dwChannelMask
= MONO
;
607 else if(out
->Format
.nChannels
== 2)
608 out
->dwChannelMask
= STEREO
;
610 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
611 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
613 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
616 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
617 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
618 if(out
->Format
.nChannels
== 1)
619 out
->dwChannelMask
= MONO
;
620 else if(out
->Format
.nChannels
== 2)
621 out
->dwChannelMask
= STEREO
;
623 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
624 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
628 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
635 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*deviceName
)
639 self
->NotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
640 self
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
641 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
643 ERR("Failed to create message events: %lu\n", GetLastError());
651 const DevMap
*iter
, *end
;
653 if(VECTOR_SIZE(PlaybackDevices
) == 0)
655 ThreadRequest req
= { self
->MsgEvent
, 0 };
656 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
657 (void)WaitForResponse(&req
);
661 iter
= VECTOR_ITER_BEGIN(PlaybackDevices
);
662 end
= VECTOR_ITER_END(PlaybackDevices
);
663 for(;iter
!= end
;iter
++)
665 if(al_string_cmp_cstr(iter
->name
, deviceName
) == 0)
667 self
->devid
= strdupW(iter
->devid
);
673 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
679 ThreadRequest req
= { self
->MsgEvent
, 0 };
682 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
683 hr
= WaitForResponse(&req
);
685 ERR("Failed to post thread message: %lu\n", GetLastError());
690 if(self
->NotifyEvent
!= NULL
)
691 CloseHandle(self
->NotifyEvent
);
692 self
->NotifyEvent
= NULL
;
693 if(self
->MsgEvent
!= NULL
)
694 CloseHandle(self
->MsgEvent
);
695 self
->MsgEvent
= NULL
;
700 ERR("Device init failed: 0x%08lx\n", hr
);
701 return ALC_INVALID_VALUE
;
707 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
)
709 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
713 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
716 IMMDeviceEnumerator
*Enumerator
= ptr
;
718 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &self
->mmdev
);
720 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
721 IMMDeviceEnumerator_Release(Enumerator
);
725 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
729 get_device_name(self
->mmdev
, &device
->DeviceName
);
735 IMMDevice_Release(self
->mmdev
);
743 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
)
745 ThreadRequest req
= { self
->MsgEvent
, 0 };
747 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
748 (void)WaitForResponse(&req
);
750 CloseHandle(self
->MsgEvent
);
751 self
->MsgEvent
= NULL
;
753 CloseHandle(self
->NotifyEvent
);
754 self
->NotifyEvent
= NULL
;
760 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
)
763 IAudioClient_Release(self
->client
);
767 IMMDevice_Release(self
->mmdev
);
772 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
)
774 ThreadRequest req
= { self
->MsgEvent
, 0 };
777 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
778 hr
= WaitForResponse(&req
);
780 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
783 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
)
785 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
786 EndpointFormFactor formfactor
= UnknownFormFactor
;
787 WAVEFORMATEXTENSIBLE OutputType
;
788 WAVEFORMATEX
*wfx
= NULL
;
789 REFERENCE_TIME min_per
, buf_time
;
790 UINT32 buffer_len
, min_len
;
795 IAudioClient_Release(self
->client
);
798 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
801 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
806 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
809 ERR("Failed to get mix format: 0x%08lx\n", hr
);
813 if(!MakeExtensible(&OutputType
, wfx
))
821 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
822 device
->Frequency
-1) / device
->Frequency
;
824 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
825 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
826 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
828 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
829 device
->FmtChans
= DevFmtMono
;
830 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
831 device
->FmtChans
= DevFmtStereo
;
832 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
833 device
->FmtChans
= DevFmtQuad
;
834 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
835 device
->FmtChans
= DevFmtX51
;
836 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
837 device
->FmtChans
= DevFmtX51Rear
;
838 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
839 device
->FmtChans
= DevFmtX61
;
840 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_NARROW
))
841 device
->FmtChans
= DevFmtX71
;
843 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
846 switch(device
->FmtChans
)
849 OutputType
.Format
.nChannels
= 1;
850 OutputType
.dwChannelMask
= MONO
;
852 case DevFmtBFormat3D
:
853 device
->FmtChans
= DevFmtStereo
;
856 OutputType
.Format
.nChannels
= 2;
857 OutputType
.dwChannelMask
= STEREO
;
860 OutputType
.Format
.nChannels
= 4;
861 OutputType
.dwChannelMask
= QUAD
;
864 OutputType
.Format
.nChannels
= 6;
865 OutputType
.dwChannelMask
= X5DOT1
;
868 OutputType
.Format
.nChannels
= 6;
869 OutputType
.dwChannelMask
= X5DOT1REAR
;
872 OutputType
.Format
.nChannels
= 7;
873 OutputType
.dwChannelMask
= X6DOT1
;
876 OutputType
.Format
.nChannels
= 8;
877 OutputType
.dwChannelMask
= X7DOT1
;
880 switch(device
->FmtType
)
883 device
->FmtType
= DevFmtUByte
;
886 OutputType
.Format
.wBitsPerSample
= 8;
887 OutputType
.Samples
.wValidBitsPerSample
= 8;
888 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
891 device
->FmtType
= DevFmtShort
;
894 OutputType
.Format
.wBitsPerSample
= 16;
895 OutputType
.Samples
.wValidBitsPerSample
= 16;
896 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
899 device
->FmtType
= DevFmtInt
;
902 OutputType
.Format
.wBitsPerSample
= 32;
903 OutputType
.Samples
.wValidBitsPerSample
= 32;
904 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
907 OutputType
.Format
.wBitsPerSample
= 32;
908 OutputType
.Samples
.wValidBitsPerSample
= 32;
909 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
912 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
914 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
915 OutputType
.Format
.wBitsPerSample
/ 8;
916 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
917 OutputType
.Format
.nBlockAlign
;
919 hr
= IAudioClient_IsFormatSupported(self
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
922 ERR("Failed to check format support: 0x%08lx\n", hr
);
923 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
927 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
933 if(!MakeExtensible(&OutputType
, wfx
))
941 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
942 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
943 device
->FmtChans
= DevFmtMono
;
944 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
945 device
->FmtChans
= DevFmtStereo
;
946 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
947 device
->FmtChans
= DevFmtQuad
;
948 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
949 device
->FmtChans
= DevFmtX51
;
950 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
951 device
->FmtChans
= DevFmtX51Rear
;
952 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
953 device
->FmtChans
= DevFmtX61
;
954 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_NARROW
))
955 device
->FmtChans
= DevFmtX71
;
958 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
959 device
->FmtChans
= DevFmtStereo
;
960 OutputType
.Format
.nChannels
= 2;
961 OutputType
.dwChannelMask
= STEREO
;
964 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
966 if(OutputType
.Format
.wBitsPerSample
== 8)
967 device
->FmtType
= DevFmtUByte
;
968 else if(OutputType
.Format
.wBitsPerSample
== 16)
969 device
->FmtType
= DevFmtShort
;
970 else if(OutputType
.Format
.wBitsPerSample
== 32)
971 device
->FmtType
= DevFmtInt
;
974 device
->FmtType
= DevFmtShort
;
975 OutputType
.Format
.wBitsPerSample
= 16;
978 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
980 device
->FmtType
= DevFmtFloat
;
981 OutputType
.Format
.wBitsPerSample
= 32;
985 ERR("Unhandled format sub-type\n");
986 device
->FmtType
= DevFmtShort
;
987 OutputType
.Format
.wBitsPerSample
= 16;
988 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
990 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
992 get_device_formfactor(self
->mmdev
, &formfactor
);
993 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&& formfactor
== Headphones
);
995 SetDefaultWFXChannelOrder(device
);
997 hr
= IAudioClient_Initialize(self
->client
, AUDCLNT_SHAREMODE_SHARED
,
998 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
999 buf_time
, 0, &OutputType
.Format
, NULL
);
1002 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1006 hr
= IAudioClient_GetDevicePeriod(self
->client
, &min_per
, NULL
);
1009 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
1010 /* Find the nearest multiple of the period size to the update size */
1011 if(min_len
< device
->UpdateSize
)
1012 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
1013 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1017 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
1021 device
->UpdateSize
= min_len
;
1022 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
1023 if(device
->NumUpdates
<= 1)
1025 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1026 device
->NumUpdates
= 2;
1027 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
1030 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1033 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1041 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
)
1043 ThreadRequest req
= { self
->MsgEvent
, 0 };
1044 HRESULT hr
= E_FAIL
;
1046 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1047 hr
= WaitForResponse(&req
);
1049 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1052 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
)
1057 ResetEvent(self
->NotifyEvent
);
1058 hr
= IAudioClient_Start(self
->client
);
1060 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1063 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioRenderClient
, &ptr
);
1068 if(althrd_create(&self
->thread
, ALCmmdevPlayback_mixerProc
, self
) != althrd_success
)
1071 IAudioRenderClient_Release(self
->render
);
1072 self
->render
= NULL
;
1073 IAudioClient_Stop(self
->client
);
1074 ERR("Failed to start thread\n");
1083 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
)
1085 ThreadRequest req
= { self
->MsgEvent
, 0 };
1086 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1087 (void)WaitForResponse(&req
);
1090 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
)
1098 althrd_join(self
->thread
, &res
);
1100 IAudioRenderClient_Release(self
->render
);
1101 self
->render
= NULL
;
1102 IAudioClient_Stop(self
->client
);
1106 static ALint64
ALCmmdevPlayback_getLatency(ALCmmdevPlayback
*self
)
1108 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1109 return (ALint64
)self
->Padding
* 1000000000 / device
->Frequency
;
1113 typedef struct ALCmmdevCapture
{
1114 DERIVE_FROM_TYPE(ALCbackend
);
1115 DERIVE_FROM_TYPE(ALCmmdevProxy
);
1120 IAudioClient
*client
;
1121 IAudioCaptureClient
*capture
;
1128 volatile int killNow
;
1132 static int ALCmmdevCapture_recordProc(void *arg
);
1134 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
);
1135 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
);
1136 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*name
);
1137 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
);
1138 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
);
1139 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
);
1140 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ALCboolean
, reset
)
1141 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
);
1142 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
);
1143 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
);
1144 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
);
1145 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
);
1146 static ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
1147 static ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
);
1148 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ALint64
, getLatency
)
1149 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, lock
)
1150 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, unlock
)
1151 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture
)
1153 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture
);
1154 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture
);
1157 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
)
1159 SET_VTABLE2(ALCmmdevCapture
, ALCbackend
, self
);
1160 SET_VTABLE2(ALCmmdevCapture
, ALCmmdevProxy
, self
);
1161 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
1162 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
1167 self
->client
= NULL
;
1168 self
->capture
= NULL
;
1169 self
->NotifyEvent
= NULL
;
1171 self
->MsgEvent
= NULL
;
1178 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
)
1180 DestroyRingBuffer(self
->Ring
);
1183 if(self
->NotifyEvent
!= NULL
)
1184 CloseHandle(self
->NotifyEvent
);
1185 self
->NotifyEvent
= NULL
;
1186 if(self
->MsgEvent
!= NULL
)
1187 CloseHandle(self
->MsgEvent
);
1188 self
->MsgEvent
= NULL
;
1193 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
1194 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
1198 FORCE_ALIGN
int ALCmmdevCapture_recordProc(void *arg
)
1200 ALCmmdevCapture
*self
= arg
;
1201 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1204 hr
= CoInitialize(NULL
);
1207 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
1208 V0(device
->Backend
,lock
)();
1209 aluHandleDisconnect(device
);
1210 V0(device
->Backend
,unlock
)();
1214 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
1216 while(!self
->killNow
)
1221 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1223 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1224 else while(avail
> 0 && SUCCEEDED(hr
))
1230 hr
= IAudioCaptureClient_GetBuffer(self
->capture
,
1231 &data
, &numsamples
, &flags
, NULL
, NULL
1235 ERR("Failed to get capture buffer: 0x%08lx\n", hr
);
1239 WriteRingBuffer(self
->Ring
, data
, numsamples
);
1241 hr
= IAudioCaptureClient_ReleaseBuffer(self
->capture
, numsamples
);
1244 ERR("Failed to release capture buffer: 0x%08lx\n", hr
);
1248 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1250 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1255 V0(device
->Backend
,lock
)();
1256 aluHandleDisconnect(device
);
1257 V0(device
->Backend
,unlock
)();
1261 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
1262 if(res
!= WAIT_OBJECT_0
)
1263 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
1271 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*deviceName
)
1275 self
->NotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1276 self
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1277 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
1279 ERR("Failed to create message events: %lu\n", GetLastError());
1289 if(VECTOR_SIZE(CaptureDevices
) == 0)
1291 ThreadRequest req
= { self
->MsgEvent
, 0 };
1292 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, CAPTURE_DEVICE_PROBE
))
1293 (void)WaitForResponse(&req
);
1297 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
1298 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1299 if(iter
== VECTOR_ITER_END(CaptureDevices
))
1300 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
1303 self
->devid
= strdupW(iter
->devid
);
1312 ThreadRequest req
= { self
->MsgEvent
, 0 };
1315 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1316 hr
= WaitForResponse(&req
);
1318 ERR("Failed to post thread message: %lu\n", GetLastError());
1323 if(self
->NotifyEvent
!= NULL
)
1324 CloseHandle(self
->NotifyEvent
);
1325 self
->NotifyEvent
= NULL
;
1326 if(self
->MsgEvent
!= NULL
)
1327 CloseHandle(self
->MsgEvent
);
1328 self
->MsgEvent
= NULL
;
1333 ERR("Device init failed: 0x%08lx\n", hr
);
1334 return ALC_INVALID_VALUE
;
1338 ThreadRequest req
= { self
->MsgEvent
, 0 };
1341 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1342 hr
= WaitForResponse(&req
);
1344 ERR("Failed to post thread message: %lu\n", GetLastError());
1348 ALCmmdevCapture_close(self
);
1349 if(hr
== E_OUTOFMEMORY
)
1350 return ALC_OUT_OF_MEMORY
;
1351 return ALC_INVALID_VALUE
;
1355 return ALC_NO_ERROR
;
1358 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
)
1360 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1364 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
1367 IMMDeviceEnumerator
*Enumerator
= ptr
;
1369 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eCapture
, eMultimedia
, &self
->mmdev
);
1371 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
1372 IMMDeviceEnumerator_Release(Enumerator
);
1376 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1380 get_device_name(self
->mmdev
, &device
->DeviceName
);
1386 IMMDevice_Release(self
->mmdev
);
1394 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
)
1396 ThreadRequest req
= { self
->MsgEvent
, 0 };
1398 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1399 (void)WaitForResponse(&req
);
1401 DestroyRingBuffer(self
->Ring
);
1404 CloseHandle(self
->MsgEvent
);
1405 self
->MsgEvent
= NULL
;
1407 CloseHandle(self
->NotifyEvent
);
1408 self
->NotifyEvent
= NULL
;
1414 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
)
1417 IAudioClient_Release(self
->client
);
1418 self
->client
= NULL
;
1421 IMMDevice_Release(self
->mmdev
);
1426 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
)
1428 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1429 WAVEFORMATEXTENSIBLE OutputType
;
1430 REFERENCE_TIME buf_time
;
1436 IAudioClient_Release(self
->client
);
1437 self
->client
= NULL
;
1439 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1442 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
1447 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
1448 device
->Frequency
-1) / device
->Frequency
;
1450 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1451 switch(device
->FmtChans
)
1454 OutputType
.Format
.nChannels
= 1;
1455 OutputType
.dwChannelMask
= MONO
;
1458 OutputType
.Format
.nChannels
= 2;
1459 OutputType
.dwChannelMask
= STEREO
;
1462 OutputType
.Format
.nChannels
= 4;
1463 OutputType
.dwChannelMask
= QUAD
;
1466 OutputType
.Format
.nChannels
= 6;
1467 OutputType
.dwChannelMask
= X5DOT1
;
1470 OutputType
.Format
.nChannels
= 6;
1471 OutputType
.dwChannelMask
= X5DOT1REAR
;
1474 OutputType
.Format
.nChannels
= 7;
1475 OutputType
.dwChannelMask
= X6DOT1
;
1478 OutputType
.Format
.nChannels
= 8;
1479 OutputType
.dwChannelMask
= X7DOT1
;
1482 case DevFmtBFormat3D
:
1485 switch(device
->FmtType
)
1488 OutputType
.Format
.wBitsPerSample
= 8;
1489 OutputType
.Samples
.wValidBitsPerSample
= 8;
1490 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1493 OutputType
.Format
.wBitsPerSample
= 16;
1494 OutputType
.Samples
.wValidBitsPerSample
= 16;
1495 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1498 OutputType
.Format
.wBitsPerSample
= 32;
1499 OutputType
.Samples
.wValidBitsPerSample
= 32;
1500 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1503 OutputType
.Format
.wBitsPerSample
= 32;
1504 OutputType
.Samples
.wValidBitsPerSample
= 32;
1505 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1511 WARN("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
1514 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1516 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1517 OutputType
.Format
.wBitsPerSample
/ 8;
1518 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1519 OutputType
.Format
.nBlockAlign
;
1521 hr
= IAudioClient_IsFormatSupported(self
->client
,
1522 AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, NULL
1526 ERR("Failed to check format support: 0x%08lx\n", hr
);
1530 hr
= IAudioClient_Initialize(self
->client
,
1531 AUDCLNT_SHAREMODE_SHARED
, AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1532 buf_time
, 0, &OutputType
.Format
, NULL
1536 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1540 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1543 ERR("Failed to get buffer size: 0x%08lx\n", hr
);
1547 buffer_len
= maxu(device
->UpdateSize
*device
->NumUpdates
, buffer_len
);
1548 self
->Ring
= CreateRingBuffer(OutputType
.Format
.nBlockAlign
, buffer_len
);
1551 ERR("Failed to allocate capture ring buffer\n");
1552 return E_OUTOFMEMORY
;
1555 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1558 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1566 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
)
1568 ThreadRequest req
= { self
->MsgEvent
, 0 };
1569 HRESULT hr
= E_FAIL
;
1571 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1572 hr
= WaitForResponse(&req
);
1574 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1577 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
)
1582 ResetEvent(self
->NotifyEvent
);
1583 hr
= IAudioClient_Start(self
->client
);
1586 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1590 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioCaptureClient
, &ptr
);
1593 self
->capture
= ptr
;
1595 if(althrd_create(&self
->thread
, ALCmmdevCapture_recordProc
, self
) != althrd_success
)
1597 ERR("Failed to start thread\n");
1598 IAudioCaptureClient_Release(self
->capture
);
1599 self
->capture
= NULL
;
1606 IAudioClient_Stop(self
->client
);
1607 IAudioClient_Reset(self
->client
);
1614 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
)
1616 ThreadRequest req
= { self
->MsgEvent
, 0 };
1617 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1618 (void)WaitForResponse(&req
);
1621 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
)
1629 althrd_join(self
->thread
, &res
);
1631 IAudioCaptureClient_Release(self
->capture
);
1632 self
->capture
= NULL
;
1633 IAudioClient_Stop(self
->client
);
1634 IAudioClient_Reset(self
->client
);
1638 ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
)
1640 return RingBufferSize(self
->Ring
);
1643 ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
1645 if(ALCmmdevCapture_availableSamples(self
) < samples
)
1646 return ALC_INVALID_VALUE
;
1647 ReadRingBuffer(self
->Ring
, buffer
, samples
);
1648 return ALC_NO_ERROR
;
1652 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1653 { AppendAllDevicesList(al_string_get_cstr(entry
->name
)); }
1654 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1655 { AppendCaptureDeviceList(al_string_get_cstr(entry
->name
)); }
1657 typedef struct ALCmmdevBackendFactory
{
1658 DERIVE_FROM_TYPE(ALCbackendFactory
);
1659 } ALCmmdevBackendFactory
;
1660 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1662 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
*self
);
1663 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
*self
);
1664 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
*self
, ALCbackend_Type type
);
1665 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
*self
, enum DevProbe type
);
1666 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
1668 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory
);
1671 static BOOL
MMDevApiLoad(void)
1673 static HRESULT InitResult
;
1677 InitResult
= E_FAIL
;
1679 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1680 if(req
.FinishedEvt
== NULL
)
1681 ERR("Failed to create event: %lu\n", GetLastError());
1684 ThreadHdl
= CreateThread(NULL
, 0, ALCmmdevProxy_messageHandler
, &req
, 0, &ThreadID
);
1685 if(ThreadHdl
!= NULL
)
1686 InitResult
= WaitForResponse(&req
);
1687 CloseHandle(req
.FinishedEvt
);
1690 return SUCCEEDED(InitResult
);
1693 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
* UNUSED(self
))
1695 VECTOR_INIT(PlaybackDevices
);
1696 VECTOR_INIT(CaptureDevices
);
1703 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
* UNUSED(self
))
1705 clear_devlist(&PlaybackDevices
);
1706 VECTOR_DEINIT(PlaybackDevices
);
1708 clear_devlist(&CaptureDevices
);
1709 VECTOR_DEINIT(CaptureDevices
);
1713 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
1714 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
1715 CloseHandle(ThreadHdl
);
1720 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1722 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
1727 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
* UNUSED(self
), enum DevProbe type
)
1729 ThreadRequest req
= { NULL
, 0 };
1731 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1732 if(req
.FinishedEvt
== NULL
)
1733 ERR("Failed to create event: %lu\n", GetLastError());
1736 HRESULT hr
= E_FAIL
;
1737 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
1738 hr
= WaitForResponse(&req
);
1739 if(SUCCEEDED(hr
)) switch(type
)
1741 case ALL_DEVICE_PROBE
:
1742 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1745 case CAPTURE_DEVICE_PROBE
:
1746 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1749 CloseHandle(req
.FinishedEvt
);
1750 req
.FinishedEvt
= NULL
;
1754 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1756 if(type
== ALCbackend_Playback
)
1758 ALCmmdevPlayback
*backend
;
1760 backend
= ALCmmdevPlayback_New(sizeof(*backend
));
1761 if(!backend
) return NULL
;
1762 memset(backend
, 0, sizeof(*backend
));
1764 ALCmmdevPlayback_Construct(backend
, device
);
1766 return STATIC_CAST(ALCbackend
, backend
);
1768 if(type
== ALCbackend_Capture
)
1770 ALCmmdevCapture
*backend
;
1772 backend
= ALCmmdevCapture_New(sizeof(*backend
));
1773 if(!backend
) return NULL
;
1774 memset(backend
, 0, sizeof(*backend
));
1776 ALCmmdevCapture_Construct(backend
, device
);
1778 return STATIC_CAST(ALCbackend
, backend
);
1785 ALCbackendFactory
*ALCmmdevBackendFactory_getFactory(void)
1787 static ALCmmdevBackendFactory factory
= ALCMMDEVBACKENDFACTORY_INITIALIZER
;
1788 return STATIC_CAST(ALCbackendFactory
, &factory
);