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
29 #include <mmdeviceapi.h>
30 #include <audioclient.h>
32 #include <devpropdef.h>
37 #ifndef _WAVEFORMATEXTENSIBLE_
44 #include "ringbuffer.h"
48 #include "converter.h"
50 #include "backends/base.h"
53 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
54 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
56 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName
, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
57 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor
, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
58 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID
, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
60 #define MONO SPEAKER_FRONT_CENTER
61 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
62 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
63 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
64 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
65 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
66 #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)
67 #define X7DOT1_WIDE (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)
69 #define REFTIME_PER_SEC ((REFERENCE_TIME)10000000)
71 #define DEVNAME_HEAD "OpenAL Soft on "
74 /* Scales the given value using 64-bit integer math, ceiling the result. */
75 static inline ALuint64
ScaleCeil(ALuint64 val
, ALuint64 new_scale
, ALuint64 old_scale
)
77 return (val
*new_scale
+ old_scale
-1) / old_scale
;
83 al_string endpoint_guid
; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
86 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
88 static void clear_devlist(vector_DevMap
*list
)
90 #define CLEAR_DEVMAP(i) do { \
91 AL_STRING_DEINIT((i)->name); \
92 AL_STRING_DEINIT((i)->endpoint_guid); \
96 VECTOR_FOR_EACH(DevMap
, *list
, CLEAR_DEVMAP
);
97 VECTOR_RESIZE(*list
, 0, 0);
101 static vector_DevMap PlaybackDevices
;
102 static vector_DevMap CaptureDevices
;
105 static HANDLE ThreadHdl
;
106 static DWORD ThreadID
;
113 #define WM_USER_First (WM_USER+0)
114 #define WM_USER_OpenDevice (WM_USER+0)
115 #define WM_USER_ResetDevice (WM_USER+1)
116 #define WM_USER_StartDevice (WM_USER+2)
117 #define WM_USER_StopDevice (WM_USER+3)
118 #define WM_USER_CloseDevice (WM_USER+4)
119 #define WM_USER_Enumerate (WM_USER+5)
120 #define WM_USER_Last (WM_USER+5)
122 static const char MessageStr
[WM_USER_Last
+1-WM_USER
][20] = {
131 static inline void ReturnMsgResponse(ThreadRequest
*req
, HRESULT res
)
134 SetEvent(req
->FinishedEvt
);
137 static HRESULT
WaitForResponse(ThreadRequest
*req
)
139 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
141 ERR("Message response error: %lu\n", GetLastError());
146 static void get_device_name_and_guid(IMMDevice
*device
, al_string
*name
, al_string
*guid
)
153 alstr_copy_cstr(name
, DEVNAME_HEAD
);
155 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
158 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
159 alstr_append_cstr(name
, "Unknown Device Name");
160 if(guid
!=NULL
)alstr_copy_cstr(guid
, "Unknown Device GUID");
164 PropVariantInit(&pvname
);
166 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
169 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr
);
170 alstr_append_cstr(name
, "Unknown Device Name");
172 else if(pvname
.vt
== VT_LPWSTR
)
173 alstr_append_wcstr(name
, pvname
.pwszVal
);
176 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname
.vt
);
177 alstr_append_cstr(name
, "Unknown Device Name");
179 PropVariantClear(&pvname
);
182 PropVariantInit(&pvguid
);
184 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&PKEY_AudioEndpoint_GUID
, &pvguid
);
187 WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr
);
188 alstr_copy_cstr(guid
, "Unknown Device GUID");
190 else if(pvguid
.vt
== VT_LPWSTR
)
191 alstr_copy_wcstr(guid
, pvguid
.pwszVal
);
194 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid
.vt
);
195 alstr_copy_cstr(guid
, "Unknown Device GUID");
198 PropVariantClear(&pvguid
);
201 IPropertyStore_Release(ps
);
204 static void get_device_formfactor(IMMDevice
*device
, EndpointFormFactor
*formfactor
)
210 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
213 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
217 PropVariantInit(&pvform
);
219 hr
= IPropertyStore_GetValue(ps
, &PKEY_AudioEndpoint_FormFactor
, &pvform
);
221 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr
);
222 else if(pvform
.vt
== VT_UI4
)
223 *formfactor
= pvform
.ulVal
;
224 else if(pvform
.vt
== VT_EMPTY
)
225 *formfactor
= UnknownFormFactor
;
227 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform
.vt
);
229 PropVariantClear(&pvform
);
230 IPropertyStore_Release(ps
);
234 static void add_device(IMMDevice
*device
, const WCHAR
*devid
, vector_DevMap
*list
)
240 AL_STRING_INIT(tmpname
);
241 AL_STRING_INIT(entry
.name
);
242 AL_STRING_INIT(entry
.endpoint_guid
);
244 entry
.devid
= strdupW(devid
);
245 get_device_name_and_guid(device
, &tmpname
, &entry
.endpoint_guid
);
251 alstr_copy(&entry
.name
, tmpname
);
255 snprintf(str
, sizeof(str
), " #%d", count
+1);
256 alstr_append_cstr(&entry
.name
, str
);
259 #define MATCH_ENTRY(i) (alstr_cmp(entry.name, (i)->name) == 0)
260 VECTOR_FIND_IF(iter
, const DevMap
, *list
, MATCH_ENTRY
);
261 if(iter
== VECTOR_END(*list
)) break;
266 TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", alstr_get_cstr(entry
.name
), alstr_get_cstr(entry
.endpoint_guid
), entry
.devid
);
267 VECTOR_PUSH_BACK(*list
, entry
);
269 AL_STRING_DEINIT(tmpname
);
272 static WCHAR
*get_device_id(IMMDevice
*device
)
277 hr
= IMMDevice_GetId(device
, &devid
);
280 ERR("Failed to get device id: %lx\n", hr
);
287 static HRESULT
probe_devices(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, vector_DevMap
*list
)
289 IMMDeviceCollection
*coll
;
290 IMMDevice
*defdev
= NULL
;
291 WCHAR
*defdevid
= NULL
;
296 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
299 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
304 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
305 if(SUCCEEDED(hr
) && count
> 0)
308 VECTOR_RESIZE(*list
, 0, count
);
310 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, flowdir
,
311 eMultimedia
, &defdev
);
313 if(SUCCEEDED(hr
) && defdev
!= NULL
)
315 defdevid
= get_device_id(defdev
);
317 add_device(defdev
, defdevid
, list
);
320 for(i
= 0;i
< count
;++i
)
325 hr
= IMMDeviceCollection_Item(coll
, i
, &device
);
326 if(FAILED(hr
)) continue;
328 devid
= get_device_id(device
);
331 if(wcscmp(devid
, defdevid
) != 0)
332 add_device(device
, devid
, list
);
333 CoTaskMemFree(devid
);
335 IMMDevice_Release(device
);
338 if(defdev
) IMMDevice_Release(defdev
);
339 if(defdevid
) CoTaskMemFree(defdevid
);
340 IMMDeviceCollection_Release(coll
);
346 /* Proxy interface used by the message handler. */
347 struct ALCmmdevProxyVtable
;
349 typedef struct ALCmmdevProxy
{
350 const struct ALCmmdevProxyVtable
*vtbl
;
353 struct ALCmmdevProxyVtable
{
354 HRESULT (*const openProxy
)(ALCmmdevProxy
*);
355 void (*const closeProxy
)(ALCmmdevProxy
*);
357 HRESULT (*const resetProxy
)(ALCmmdevProxy
*);
358 HRESULT (*const startProxy
)(ALCmmdevProxy
*);
359 void (*const stopProxy
)(ALCmmdevProxy
*);
362 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
363 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
364 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
365 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
366 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
367 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
369 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
370 T##_ALCmmdevProxy_openProxy, \
371 T##_ALCmmdevProxy_closeProxy, \
372 T##_ALCmmdevProxy_resetProxy, \
373 T##_ALCmmdevProxy_startProxy, \
374 T##_ALCmmdevProxy_stopProxy, \
377 static void ALCmmdevProxy_Construct(ALCmmdevProxy
* UNUSED(self
)) { }
378 static void ALCmmdevProxy_Destruct(ALCmmdevProxy
* UNUSED(self
)) { }
380 static DWORD CALLBACK
ALCmmdevProxy_messageHandler(void *ptr
)
382 ThreadRequest
*req
= ptr
;
383 IMMDeviceEnumerator
*Enumerator
;
384 ALuint deviceCount
= 0;
385 ALCmmdevProxy
*proxy
;
389 TRACE("Starting message thread\n");
391 cohr
= CoInitialize(NULL
);
394 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
395 ReturnMsgResponse(req
, cohr
);
399 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
402 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
404 ReturnMsgResponse(req
, hr
);
408 IMMDeviceEnumerator_Release(Enumerator
);
413 /* HACK: Force Windows to create a message queue for this thread before
414 * returning success, otherwise PostThreadMessage may fail if it gets
415 * called before GetMessage.
417 PeekMessage(&msg
, NULL
, WM_USER
, WM_USER
, PM_NOREMOVE
);
419 TRACE("Message thread initialization complete\n");
420 ReturnMsgResponse(req
, S_OK
);
422 TRACE("Starting message loop\n");
423 while(GetMessage(&msg
, NULL
, WM_USER_First
, WM_USER_Last
))
425 TRACE("Got message \"%s\" (0x%04x, lparam=%p, wparam=%p)\n",
426 (msg
.message
>= WM_USER
&& msg
.message
<= WM_USER_Last
) ?
427 MessageStr
[msg
.message
-WM_USER
] : "Unknown",
428 msg
.message
, (void*)msg
.lParam
, (void*)msg
.wParam
432 case WM_USER_OpenDevice
:
433 req
= (ThreadRequest
*)msg
.wParam
;
434 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
437 if(++deviceCount
== 1)
438 hr
= cohr
= CoInitialize(NULL
);
440 hr
= V0(proxy
,openProxy
)();
443 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
447 ReturnMsgResponse(req
, hr
);
450 case WM_USER_ResetDevice
:
451 req
= (ThreadRequest
*)msg
.wParam
;
452 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
454 hr
= V0(proxy
,resetProxy
)();
455 ReturnMsgResponse(req
, hr
);
458 case WM_USER_StartDevice
:
459 req
= (ThreadRequest
*)msg
.wParam
;
460 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
462 hr
= V0(proxy
,startProxy
)();
463 ReturnMsgResponse(req
, hr
);
466 case WM_USER_StopDevice
:
467 req
= (ThreadRequest
*)msg
.wParam
;
468 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
470 V0(proxy
,stopProxy
)();
471 ReturnMsgResponse(req
, S_OK
);
474 case WM_USER_CloseDevice
:
475 req
= (ThreadRequest
*)msg
.wParam
;
476 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
478 V0(proxy
,closeProxy
)();
479 if(--deviceCount
== 0)
482 ReturnMsgResponse(req
, S_OK
);
485 case WM_USER_Enumerate
:
486 req
= (ThreadRequest
*)msg
.wParam
;
489 if(++deviceCount
== 1)
490 hr
= cohr
= CoInitialize(NULL
);
492 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
497 if(msg
.lParam
== ALL_DEVICE_PROBE
)
498 hr
= probe_devices(Enumerator
, eRender
, &PlaybackDevices
);
499 else if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
500 hr
= probe_devices(Enumerator
, eCapture
, &CaptureDevices
);
502 IMMDeviceEnumerator_Release(Enumerator
);
506 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
509 ReturnMsgResponse(req
, hr
);
513 ERR("Unexpected message: %u\n", msg
.message
);
517 TRACE("Message loop finished\n");
523 typedef struct ALCmmdevPlayback
{
524 DERIVE_FROM_TYPE(ALCbackend
);
525 DERIVE_FROM_TYPE(ALCmmdevProxy
);
530 IAudioClient
*client
;
531 IAudioRenderClient
*render
;
536 ATOMIC(UINT32
) Padding
;
542 static int ALCmmdevPlayback_mixerProc(void *arg
);
544 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
);
545 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
);
546 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*name
);
547 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
);
548 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
);
549 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
);
550 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
);
551 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
);
552 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
);
553 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
);
554 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
);
555 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
);
556 static DECLARE_FORWARD2(ALCmmdevPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
557 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, ALCuint
, availableSamples
)
558 static ClockLatency
ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback
*self
);
559 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, lock
)
560 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, unlock
)
561 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback
)
563 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback
);
564 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback
);
567 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
)
569 SET_VTABLE2(ALCmmdevPlayback
, ALCbackend
, self
);
570 SET_VTABLE2(ALCmmdevPlayback
, ALCmmdevProxy
, self
);
571 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
572 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
579 self
->NotifyEvent
= NULL
;
581 self
->MsgEvent
= NULL
;
583 ATOMIC_INIT(&self
->Padding
, 0);
585 ATOMIC_INIT(&self
->killNow
, 0);
588 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
)
590 ALCmmdevPlayback_close(self
);
592 if(self
->NotifyEvent
!= NULL
)
593 CloseHandle(self
->NotifyEvent
);
594 self
->NotifyEvent
= NULL
;
595 if(self
->MsgEvent
!= NULL
)
596 CloseHandle(self
->MsgEvent
);
597 self
->MsgEvent
= NULL
;
602 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
603 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
607 FORCE_ALIGN
static int ALCmmdevPlayback_mixerProc(void *arg
)
609 ALCmmdevPlayback
*self
= arg
;
610 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
611 UINT32 buffer_len
, written
;
612 ALuint update_size
, len
;
616 hr
= CoInitialize(NULL
);
619 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
620 V0(device
->Backend
,lock
)();
621 aluHandleDisconnect(device
);
622 V0(device
->Backend
,unlock
)();
627 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
629 update_size
= device
->UpdateSize
;
630 buffer_len
= update_size
* device
->NumUpdates
;
631 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_relaxed
))
633 hr
= IAudioClient_GetCurrentPadding(self
->client
, &written
);
636 ERR("Failed to get padding: 0x%08lx\n", hr
);
637 V0(device
->Backend
,lock
)();
638 aluHandleDisconnect(device
);
639 V0(device
->Backend
,unlock
)();
642 ATOMIC_STORE(&self
->Padding
, written
, almemory_order_relaxed
);
644 len
= buffer_len
- written
;
645 if(len
< update_size
)
648 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
649 if(res
!= WAIT_OBJECT_0
)
650 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
653 len
-= len
%update_size
;
655 hr
= IAudioRenderClient_GetBuffer(self
->render
, len
, &buffer
);
658 ALCmmdevPlayback_lock(self
);
659 aluMixData(device
, buffer
, len
);
660 ATOMIC_STORE(&self
->Padding
, written
+ len
, almemory_order_relaxed
);
661 ALCmmdevPlayback_unlock(self
);
662 hr
= IAudioRenderClient_ReleaseBuffer(self
->render
, len
, 0);
666 ERR("Failed to buffer data: 0x%08lx\n", hr
);
667 V0(device
->Backend
,lock
)();
668 aluHandleDisconnect(device
);
669 V0(device
->Backend
,unlock
)();
673 ATOMIC_STORE(&self
->Padding
, 0, almemory_order_release
);
680 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
682 memset(out
, 0, sizeof(*out
));
683 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
684 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
685 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
688 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
689 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
690 if(out
->Format
.nChannels
== 1)
691 out
->dwChannelMask
= MONO
;
692 else if(out
->Format
.nChannels
== 2)
693 out
->dwChannelMask
= STEREO
;
695 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
696 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
698 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
701 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
702 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
703 if(out
->Format
.nChannels
== 1)
704 out
->dwChannelMask
= MONO
;
705 else if(out
->Format
.nChannels
== 2)
706 out
->dwChannelMask
= STEREO
;
708 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
709 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
713 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
719 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*deviceName
)
723 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
724 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
725 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
727 ERR("Failed to create message events: %lu\n", GetLastError());
737 if(VECTOR_SIZE(PlaybackDevices
) == 0)
739 ThreadRequest req
= { self
->MsgEvent
, 0 };
740 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
741 (void)WaitForResponse(&req
);
745 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
746 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
747 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
749 if(iter
== VECTOR_END(PlaybackDevices
))
752 if((len
=MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, NULL
, 0)) > 0)
754 WCHAR
*wname
= calloc(sizeof(WCHAR
), len
);
755 MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, wname
, len
);
756 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
757 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
762 if(iter
== VECTOR_END(PlaybackDevices
))
763 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
766 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
767 self
->devid
= strdupW(iter
->devid
);
768 alstr_copy(&device
->DeviceName
, iter
->name
);
776 ThreadRequest req
= { self
->MsgEvent
, 0 };
779 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
780 hr
= WaitForResponse(&req
);
782 ERR("Failed to post thread message: %lu\n", GetLastError());
787 if(self
->NotifyEvent
!= NULL
)
788 CloseHandle(self
->NotifyEvent
);
789 self
->NotifyEvent
= NULL
;
790 if(self
->MsgEvent
!= NULL
)
791 CloseHandle(self
->MsgEvent
);
792 self
->MsgEvent
= NULL
;
797 ERR("Device init failed: 0x%08lx\n", hr
);
798 return ALC_INVALID_VALUE
;
804 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
)
806 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
810 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
813 IMMDeviceEnumerator
*Enumerator
= ptr
;
815 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &self
->mmdev
);
817 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
818 IMMDeviceEnumerator_Release(Enumerator
);
822 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
826 if(alstr_empty(device
->DeviceName
))
827 get_device_name_and_guid(self
->mmdev
, &device
->DeviceName
, NULL
);
833 IMMDevice_Release(self
->mmdev
);
841 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
)
843 ThreadRequest req
= { self
->MsgEvent
, 0 };
848 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
849 (void)WaitForResponse(&req
);
851 CloseHandle(self
->MsgEvent
);
852 self
->MsgEvent
= NULL
;
854 CloseHandle(self
->NotifyEvent
);
855 self
->NotifyEvent
= NULL
;
861 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
)
864 IAudioClient_Release(self
->client
);
868 IMMDevice_Release(self
->mmdev
);
873 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
)
875 ThreadRequest req
= { self
->MsgEvent
, 0 };
878 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
879 hr
= WaitForResponse(&req
);
881 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
884 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
)
886 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
887 EndpointFormFactor formfactor
= UnknownFormFactor
;
888 WAVEFORMATEXTENSIBLE OutputType
;
889 WAVEFORMATEX
*wfx
= NULL
;
890 REFERENCE_TIME min_per
, buf_time
;
891 UINT32 buffer_len
, min_len
;
896 IAudioClient_Release(self
->client
);
899 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
902 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
907 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
910 ERR("Failed to get mix format: 0x%08lx\n", hr
);
914 if(!MakeExtensible(&OutputType
, wfx
))
922 buf_time
= ScaleCeil(device
->UpdateSize
*device
->NumUpdates
, REFTIME_PER_SEC
,
925 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
926 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
927 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
929 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
930 device
->FmtChans
= DevFmtMono
;
931 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
932 device
->FmtChans
= DevFmtStereo
;
933 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
934 device
->FmtChans
= DevFmtQuad
;
935 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
936 device
->FmtChans
= DevFmtX51
;
937 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
938 device
->FmtChans
= DevFmtX51Rear
;
939 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
940 device
->FmtChans
= DevFmtX61
;
941 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
942 device
->FmtChans
= DevFmtX71
;
944 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
947 switch(device
->FmtChans
)
950 OutputType
.Format
.nChannels
= 1;
951 OutputType
.dwChannelMask
= MONO
;
954 device
->FmtChans
= DevFmtStereo
;
957 OutputType
.Format
.nChannels
= 2;
958 OutputType
.dwChannelMask
= STEREO
;
961 OutputType
.Format
.nChannels
= 4;
962 OutputType
.dwChannelMask
= QUAD
;
965 OutputType
.Format
.nChannels
= 6;
966 OutputType
.dwChannelMask
= X5DOT1
;
969 OutputType
.Format
.nChannels
= 6;
970 OutputType
.dwChannelMask
= X5DOT1REAR
;
973 OutputType
.Format
.nChannels
= 7;
974 OutputType
.dwChannelMask
= X6DOT1
;
977 OutputType
.Format
.nChannels
= 8;
978 OutputType
.dwChannelMask
= X7DOT1
;
981 switch(device
->FmtType
)
984 device
->FmtType
= DevFmtUByte
;
987 OutputType
.Format
.wBitsPerSample
= 8;
988 OutputType
.Samples
.wValidBitsPerSample
= 8;
989 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
992 device
->FmtType
= DevFmtShort
;
995 OutputType
.Format
.wBitsPerSample
= 16;
996 OutputType
.Samples
.wValidBitsPerSample
= 16;
997 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1000 device
->FmtType
= DevFmtInt
;
1003 OutputType
.Format
.wBitsPerSample
= 32;
1004 OutputType
.Samples
.wValidBitsPerSample
= 32;
1005 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1008 OutputType
.Format
.wBitsPerSample
= 32;
1009 OutputType
.Samples
.wValidBitsPerSample
= 32;
1010 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1013 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1015 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1016 OutputType
.Format
.wBitsPerSample
/ 8;
1017 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1018 OutputType
.Format
.nBlockAlign
;
1020 hr
= IAudioClient_IsFormatSupported(self
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
1023 ERR("Failed to check format support: 0x%08lx\n", hr
);
1024 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
1028 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
1034 if(!MakeExtensible(&OutputType
, wfx
))
1042 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
1043 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
1044 device
->FmtChans
= DevFmtMono
;
1045 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
1046 device
->FmtChans
= DevFmtStereo
;
1047 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
1048 device
->FmtChans
= DevFmtQuad
;
1049 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
1050 device
->FmtChans
= DevFmtX51
;
1051 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
1052 device
->FmtChans
= DevFmtX51Rear
;
1053 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
1054 device
->FmtChans
= DevFmtX61
;
1055 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
1056 device
->FmtChans
= DevFmtX71
;
1059 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
1060 device
->FmtChans
= DevFmtStereo
;
1061 OutputType
.Format
.nChannels
= 2;
1062 OutputType
.dwChannelMask
= STEREO
;
1065 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
1067 if(OutputType
.Format
.wBitsPerSample
== 8)
1068 device
->FmtType
= DevFmtUByte
;
1069 else if(OutputType
.Format
.wBitsPerSample
== 16)
1070 device
->FmtType
= DevFmtShort
;
1071 else if(OutputType
.Format
.wBitsPerSample
== 32)
1072 device
->FmtType
= DevFmtInt
;
1075 device
->FmtType
= DevFmtShort
;
1076 OutputType
.Format
.wBitsPerSample
= 16;
1079 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
1081 device
->FmtType
= DevFmtFloat
;
1082 OutputType
.Format
.wBitsPerSample
= 32;
1086 ERR("Unhandled format sub-type\n");
1087 device
->FmtType
= DevFmtShort
;
1088 OutputType
.Format
.wBitsPerSample
= 16;
1089 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1091 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
1093 get_device_formfactor(self
->mmdev
, &formfactor
);
1094 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&&
1095 (formfactor
== Headphones
|| formfactor
== Headset
)
1098 SetDefaultWFXChannelOrder(device
);
1100 hr
= IAudioClient_Initialize(self
->client
, AUDCLNT_SHAREMODE_SHARED
,
1101 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1102 buf_time
, 0, &OutputType
.Format
, NULL
);
1105 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1109 hr
= IAudioClient_GetDevicePeriod(self
->client
, &min_per
, NULL
);
1112 min_len
= (UINT32
)ScaleCeil(min_per
, device
->Frequency
, REFTIME_PER_SEC
);
1113 /* Find the nearest multiple of the period size to the update size */
1114 if(min_len
< device
->UpdateSize
)
1115 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
1116 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1120 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
1124 device
->UpdateSize
= min_len
;
1125 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
1126 if(device
->NumUpdates
<= 1)
1128 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1129 device
->NumUpdates
= 2;
1130 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
1133 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1136 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1144 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
)
1146 ThreadRequest req
= { self
->MsgEvent
, 0 };
1147 HRESULT hr
= E_FAIL
;
1149 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1150 hr
= WaitForResponse(&req
);
1152 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1155 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
)
1160 ResetEvent(self
->NotifyEvent
);
1161 hr
= IAudioClient_Start(self
->client
);
1163 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1166 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioRenderClient
, &ptr
);
1170 ATOMIC_STORE(&self
->killNow
, 0, almemory_order_release
);
1171 if(althrd_create(&self
->thread
, ALCmmdevPlayback_mixerProc
, self
) != althrd_success
)
1174 IAudioRenderClient_Release(self
->render
);
1175 self
->render
= NULL
;
1176 IAudioClient_Stop(self
->client
);
1177 ERR("Failed to start thread\n");
1186 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
)
1188 ThreadRequest req
= { self
->MsgEvent
, 0 };
1189 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1190 (void)WaitForResponse(&req
);
1193 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
)
1200 ATOMIC_STORE_SEQ(&self
->killNow
, 1);
1201 althrd_join(self
->thread
, &res
);
1203 IAudioRenderClient_Release(self
->render
);
1204 self
->render
= NULL
;
1205 IAudioClient_Stop(self
->client
);
1209 static ClockLatency
ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback
*self
)
1211 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1214 ALCmmdevPlayback_lock(self
);
1215 ret
.ClockTime
= GetDeviceClockTime(device
);
1216 ret
.Latency
= ATOMIC_LOAD(&self
->Padding
, almemory_order_relaxed
) * DEVICE_CLOCK_RES
/
1218 ALCmmdevPlayback_unlock(self
);
1224 typedef struct ALCmmdevCapture
{
1225 DERIVE_FROM_TYPE(ALCbackend
);
1226 DERIVE_FROM_TYPE(ALCmmdevProxy
);
1231 IAudioClient
*client
;
1232 IAudioCaptureClient
*capture
;
1237 ChannelConverter
*ChannelConv
;
1238 SampleConverter
*SampleConv
;
1239 ll_ringbuffer_t
*Ring
;
1241 ATOMIC(int) killNow
;
1245 static int ALCmmdevCapture_recordProc(void *arg
);
1247 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
);
1248 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
);
1249 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*name
);
1250 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
);
1251 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
);
1252 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
);
1253 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ALCboolean
, reset
)
1254 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
);
1255 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
);
1256 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
);
1257 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
);
1258 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
);
1259 static ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
1260 static ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
);
1261 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
1262 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, lock
)
1263 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, unlock
)
1264 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture
)
1266 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture
);
1267 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture
);
1270 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
)
1272 SET_VTABLE2(ALCmmdevCapture
, ALCbackend
, self
);
1273 SET_VTABLE2(ALCmmdevCapture
, ALCmmdevProxy
, self
);
1274 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
1275 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
1280 self
->client
= NULL
;
1281 self
->capture
= NULL
;
1282 self
->NotifyEvent
= NULL
;
1284 self
->MsgEvent
= NULL
;
1286 self
->ChannelConv
= NULL
;
1287 self
->SampleConv
= NULL
;
1290 ATOMIC_INIT(&self
->killNow
, 0);
1293 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
)
1295 ALCmmdevCapture_close(self
);
1297 ll_ringbuffer_free(self
->Ring
);
1300 DestroySampleConverter(&self
->SampleConv
);
1301 DestroyChannelConverter(&self
->ChannelConv
);
1303 if(self
->NotifyEvent
!= NULL
)
1304 CloseHandle(self
->NotifyEvent
);
1305 self
->NotifyEvent
= NULL
;
1306 if(self
->MsgEvent
!= NULL
)
1307 CloseHandle(self
->MsgEvent
);
1308 self
->MsgEvent
= NULL
;
1313 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
1314 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
1318 FORCE_ALIGN
int ALCmmdevCapture_recordProc(void *arg
)
1320 ALCmmdevCapture
*self
= arg
;
1321 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1322 ALfloat
*samples
= NULL
;
1323 size_t samplesmax
= 0;
1326 hr
= CoInitialize(NULL
);
1329 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
1330 V0(device
->Backend
,lock
)();
1331 aluHandleDisconnect(device
);
1332 V0(device
->Backend
,unlock
)();
1336 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
1338 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_relaxed
))
1343 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1345 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1352 hr
= IAudioCaptureClient_GetBuffer(self
->capture
,
1353 &rdata
, &numsamples
, &flags
, NULL
, NULL
1356 ERR("Failed to get capture buffer: 0x%08lx\n", hr
);
1359 ll_ringbuffer_data_t data
[2];
1360 size_t dstframes
= 0;
1362 if(self
->ChannelConv
)
1364 if(samplesmax
< numsamples
)
1366 size_t newmax
= RoundUp(numsamples
, 4096);
1367 ALfloat
*tmp
= al_calloc(DEF_ALIGN
, newmax
*2*sizeof(ALfloat
));
1370 samplesmax
= newmax
;
1372 ChannelConverterInput(self
->ChannelConv
, rdata
, samples
, numsamples
);
1373 rdata
= (BYTE
*)samples
;
1376 ll_ringbuffer_get_write_vector(self
->Ring
, data
);
1378 if(self
->SampleConv
)
1380 const ALvoid
*srcdata
= rdata
;
1381 ALsizei srcframes
= numsamples
;
1383 dstframes
= SampleConverterInput(self
->SampleConv
,
1384 &srcdata
, &srcframes
, data
[0].buf
, (ALsizei
)minz(data
[0].len
, INT_MAX
)
1386 if(srcframes
> 0 && dstframes
== data
[0].len
&& data
[1].len
> 0)
1388 /* If some source samples remain, all of the first dest
1389 * block was filled, and there's space in the second
1390 * dest block, do another run for the second block.
1392 dstframes
+= SampleConverterInput(self
->SampleConv
,
1393 &srcdata
, &srcframes
, data
[1].buf
, (ALsizei
)minz(data
[1].len
, INT_MAX
)
1399 ALuint framesize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
,
1401 size_t len1
= minz(data
[0].len
, numsamples
);
1402 size_t len2
= minz(data
[1].len
, numsamples
-len1
);
1404 memcpy(data
[0].buf
, rdata
, len1
*framesize
);
1406 memcpy(data
[1].buf
, rdata
+len1
*framesize
, len2
*framesize
);
1407 dstframes
= len1
+ len2
;
1410 ll_ringbuffer_write_advance(self
->Ring
, dstframes
);
1412 hr
= IAudioCaptureClient_ReleaseBuffer(self
->capture
, numsamples
);
1413 if(FAILED(hr
)) ERR("Failed to release capture buffer: 0x%08lx\n", hr
);
1419 V0(device
->Backend
,lock
)();
1420 aluHandleDisconnect(device
);
1421 V0(device
->Backend
,unlock
)();
1425 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
1426 if(res
!= WAIT_OBJECT_0
)
1427 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
1439 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*deviceName
)
1443 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1444 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1445 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
1447 ERR("Failed to create message events: %lu\n", GetLastError());
1457 if(VECTOR_SIZE(CaptureDevices
) == 0)
1459 ThreadRequest req
= { self
->MsgEvent
, 0 };
1460 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, CAPTURE_DEVICE_PROBE
))
1461 (void)WaitForResponse(&req
);
1465 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
1466 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
1467 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1469 if(iter
== VECTOR_END(CaptureDevices
))
1472 if((len
=MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, NULL
, 0)) > 0)
1474 WCHAR
*wname
= calloc(sizeof(WCHAR
), len
);
1475 MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, wname
, len
);
1476 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
1477 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1482 if(iter
== VECTOR_END(CaptureDevices
))
1483 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
1486 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
1487 self
->devid
= strdupW(iter
->devid
);
1488 alstr_copy(&device
->DeviceName
, iter
->name
);
1496 ThreadRequest req
= { self
->MsgEvent
, 0 };
1499 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1500 hr
= WaitForResponse(&req
);
1502 ERR("Failed to post thread message: %lu\n", GetLastError());
1507 if(self
->NotifyEvent
!= NULL
)
1508 CloseHandle(self
->NotifyEvent
);
1509 self
->NotifyEvent
= NULL
;
1510 if(self
->MsgEvent
!= NULL
)
1511 CloseHandle(self
->MsgEvent
);
1512 self
->MsgEvent
= NULL
;
1517 ERR("Device init failed: 0x%08lx\n", hr
);
1518 return ALC_INVALID_VALUE
;
1522 ThreadRequest req
= { self
->MsgEvent
, 0 };
1525 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1526 hr
= WaitForResponse(&req
);
1528 ERR("Failed to post thread message: %lu\n", GetLastError());
1532 ALCmmdevCapture_close(self
);
1533 if(hr
== E_OUTOFMEMORY
)
1534 return ALC_OUT_OF_MEMORY
;
1535 return ALC_INVALID_VALUE
;
1539 return ALC_NO_ERROR
;
1542 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
)
1544 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1548 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
1551 IMMDeviceEnumerator
*Enumerator
= ptr
;
1553 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eCapture
, eMultimedia
, &self
->mmdev
);
1555 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
1556 IMMDeviceEnumerator_Release(Enumerator
);
1560 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1564 if(alstr_empty(device
->DeviceName
))
1565 get_device_name_and_guid(self
->mmdev
, &device
->DeviceName
, NULL
);
1571 IMMDevice_Release(self
->mmdev
);
1579 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
)
1581 ThreadRequest req
= { self
->MsgEvent
, 0 };
1583 if(!req
.FinishedEvt
)
1586 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1587 (void)WaitForResponse(&req
);
1589 ll_ringbuffer_free(self
->Ring
);
1592 CloseHandle(self
->MsgEvent
);
1593 self
->MsgEvent
= NULL
;
1595 CloseHandle(self
->NotifyEvent
);
1596 self
->NotifyEvent
= NULL
;
1602 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
)
1605 IAudioClient_Release(self
->client
);
1606 self
->client
= NULL
;
1609 IMMDevice_Release(self
->mmdev
);
1614 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
)
1616 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1617 WAVEFORMATEXTENSIBLE OutputType
;
1618 WAVEFORMATEX
*wfx
= NULL
;
1619 enum DevFmtType srcType
;
1620 REFERENCE_TIME buf_time
;
1626 IAudioClient_Release(self
->client
);
1627 self
->client
= NULL
;
1629 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1632 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
1637 buf_time
= ScaleCeil(device
->UpdateSize
*device
->NumUpdates
, REFTIME_PER_SEC
,
1639 // Make sure buffer is at least 100ms in size
1640 buf_time
= maxu64(buf_time
, REFTIME_PER_SEC
/10);
1641 device
->UpdateSize
= (ALuint
)ScaleCeil(buf_time
, device
->Frequency
, REFTIME_PER_SEC
) /
1644 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1645 switch(device
->FmtChans
)
1648 OutputType
.Format
.nChannels
= 1;
1649 OutputType
.dwChannelMask
= MONO
;
1652 OutputType
.Format
.nChannels
= 2;
1653 OutputType
.dwChannelMask
= STEREO
;
1656 OutputType
.Format
.nChannels
= 4;
1657 OutputType
.dwChannelMask
= QUAD
;
1660 OutputType
.Format
.nChannels
= 6;
1661 OutputType
.dwChannelMask
= X5DOT1
;
1664 OutputType
.Format
.nChannels
= 6;
1665 OutputType
.dwChannelMask
= X5DOT1REAR
;
1668 OutputType
.Format
.nChannels
= 7;
1669 OutputType
.dwChannelMask
= X6DOT1
;
1672 OutputType
.Format
.nChannels
= 8;
1673 OutputType
.dwChannelMask
= X7DOT1
;
1679 switch(device
->FmtType
)
1681 /* NOTE: Signedness doesn't matter, the converter will handle it. */
1684 OutputType
.Format
.wBitsPerSample
= 8;
1685 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1689 OutputType
.Format
.wBitsPerSample
= 16;
1690 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1694 OutputType
.Format
.wBitsPerSample
= 32;
1695 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1698 OutputType
.Format
.wBitsPerSample
= 32;
1699 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1702 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
1703 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1705 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1706 OutputType
.Format
.wBitsPerSample
/ 8;
1707 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1708 OutputType
.Format
.nBlockAlign
;
1709 OutputType
.Format
.cbSize
= sizeof(OutputType
) - sizeof(OutputType
.Format
);
1711 hr
= IAudioClient_IsFormatSupported(self
->client
,
1712 AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
1716 ERR("Failed to check format support: 0x%08lx\n", hr
);
1720 DestroySampleConverter(&self
->SampleConv
);
1721 DestroyChannelConverter(&self
->ChannelConv
);
1725 if(!(wfx
->nChannels
== OutputType
.Format
.nChannels
||
1726 (wfx
->nChannels
== 1 && OutputType
.Format
.nChannels
== 2) ||
1727 (wfx
->nChannels
== 2 && OutputType
.Format
.nChannels
== 1)))
1729 ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
1730 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1731 device
->Frequency
, wfx
->nChannels
, (wfx
->nChannels
==1)?"":"s", wfx
->wBitsPerSample
,
1732 wfx
->nSamplesPerSec
);
1737 if(!MakeExtensible(&OutputType
, wfx
))
1746 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
1748 if(OutputType
.Format
.wBitsPerSample
== 8)
1749 srcType
= DevFmtUByte
;
1750 else if(OutputType
.Format
.wBitsPerSample
== 16)
1751 srcType
= DevFmtShort
;
1752 else if(OutputType
.Format
.wBitsPerSample
== 32)
1753 srcType
= DevFmtInt
;
1756 ERR("Unhandled integer bit depth: %d\n", OutputType
.Format
.wBitsPerSample
);
1760 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
1762 if(OutputType
.Format
.wBitsPerSample
== 32)
1763 srcType
= DevFmtFloat
;
1766 ERR("Unhandled float bit depth: %d\n", OutputType
.Format
.wBitsPerSample
);
1772 ERR("Unhandled format sub-type\n");
1776 if(device
->FmtChans
== DevFmtMono
&& OutputType
.Format
.nChannels
== 2)
1778 self
->ChannelConv
= CreateChannelConverter(srcType
, DevFmtStereo
,
1780 if(!self
->ChannelConv
)
1782 ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType
));
1785 TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType
));
1786 /* The channel converter always outputs float, so change the input type
1787 * for the resampler/type-converter.
1789 srcType
= DevFmtFloat
;
1791 else if(device
->FmtChans
== DevFmtStereo
&& OutputType
.Format
.nChannels
== 1)
1793 self
->ChannelConv
= CreateChannelConverter(srcType
, DevFmtMono
,
1795 if(!self
->ChannelConv
)
1797 ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType
));
1800 TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType
));
1801 srcType
= DevFmtFloat
;
1804 if(device
->Frequency
!= OutputType
.Format
.nSamplesPerSec
|| device
->FmtType
!= srcType
)
1806 self
->SampleConv
= CreateSampleConverter(
1807 srcType
, device
->FmtType
, ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
),
1808 OutputType
.Format
.nSamplesPerSec
, device
->Frequency
1810 if(!self
->SampleConv
)
1812 ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1813 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1814 device
->Frequency
, DevFmtTypeString(srcType
), OutputType
.Format
.nSamplesPerSec
);
1817 TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1818 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1819 device
->Frequency
, DevFmtTypeString(srcType
), OutputType
.Format
.nSamplesPerSec
);
1822 hr
= IAudioClient_Initialize(self
->client
,
1823 AUDCLNT_SHAREMODE_SHARED
, AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1824 buf_time
, 0, &OutputType
.Format
, NULL
1828 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1832 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1835 ERR("Failed to get buffer size: 0x%08lx\n", hr
);
1839 buffer_len
= maxu(device
->UpdateSize
*device
->NumUpdates
+ 1, buffer_len
);
1840 ll_ringbuffer_free(self
->Ring
);
1841 self
->Ring
= ll_ringbuffer_create(buffer_len
,
1842 FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
)
1846 ERR("Failed to allocate capture ring buffer\n");
1847 return E_OUTOFMEMORY
;
1850 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1853 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1861 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
)
1863 ThreadRequest req
= { self
->MsgEvent
, 0 };
1864 HRESULT hr
= E_FAIL
;
1866 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1867 hr
= WaitForResponse(&req
);
1869 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1872 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
)
1877 ResetEvent(self
->NotifyEvent
);
1878 hr
= IAudioClient_Start(self
->client
);
1881 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1885 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioCaptureClient
, &ptr
);
1888 self
->capture
= ptr
;
1889 ATOMIC_STORE(&self
->killNow
, 0, almemory_order_release
);
1890 if(althrd_create(&self
->thread
, ALCmmdevCapture_recordProc
, self
) != althrd_success
)
1892 ERR("Failed to start thread\n");
1893 IAudioCaptureClient_Release(self
->capture
);
1894 self
->capture
= NULL
;
1901 IAudioClient_Stop(self
->client
);
1902 IAudioClient_Reset(self
->client
);
1909 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
)
1911 ThreadRequest req
= { self
->MsgEvent
, 0 };
1912 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1913 (void)WaitForResponse(&req
);
1916 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
)
1923 ATOMIC_STORE_SEQ(&self
->killNow
, 1);
1924 althrd_join(self
->thread
, &res
);
1926 IAudioCaptureClient_Release(self
->capture
);
1927 self
->capture
= NULL
;
1928 IAudioClient_Stop(self
->client
);
1929 IAudioClient_Reset(self
->client
);
1933 ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
)
1935 return (ALuint
)ll_ringbuffer_read_space(self
->Ring
);
1938 ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
1940 if(ALCmmdevCapture_availableSamples(self
) < samples
)
1941 return ALC_INVALID_VALUE
;
1942 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
1943 return ALC_NO_ERROR
;
1947 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1948 { AppendAllDevicesList(alstr_get_cstr(entry
->name
)); }
1949 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1950 { AppendCaptureDeviceList(alstr_get_cstr(entry
->name
)); }
1952 typedef struct ALCmmdevBackendFactory
{
1953 DERIVE_FROM_TYPE(ALCbackendFactory
);
1954 } ALCmmdevBackendFactory
;
1955 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1957 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
*self
);
1958 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
*self
);
1959 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
*self
, ALCbackend_Type type
);
1960 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
*self
, enum DevProbe type
);
1961 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
1963 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory
);
1966 static BOOL
MMDevApiLoad(void)
1968 static HRESULT InitResult
;
1972 InitResult
= E_FAIL
;
1974 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1975 if(req
.FinishedEvt
== NULL
)
1976 ERR("Failed to create event: %lu\n", GetLastError());
1979 ThreadHdl
= CreateThread(NULL
, 0, ALCmmdevProxy_messageHandler
, &req
, 0, &ThreadID
);
1980 if(ThreadHdl
!= NULL
)
1981 InitResult
= WaitForResponse(&req
);
1982 CloseHandle(req
.FinishedEvt
);
1985 return SUCCEEDED(InitResult
);
1988 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
* UNUSED(self
))
1990 VECTOR_INIT(PlaybackDevices
);
1991 VECTOR_INIT(CaptureDevices
);
1998 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
* UNUSED(self
))
2000 clear_devlist(&PlaybackDevices
);
2001 VECTOR_DEINIT(PlaybackDevices
);
2003 clear_devlist(&CaptureDevices
);
2004 VECTOR_DEINIT(CaptureDevices
);
2008 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
2009 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
2010 CloseHandle(ThreadHdl
);
2015 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
2017 /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
2018 * rechanneling or resampling; if the device is configured for 48000hz
2019 * stereo input, for example, and the app asks for 22050hz mono,
2020 * initialization will fail.
2022 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
2027 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
* UNUSED(self
), enum DevProbe type
)
2029 ThreadRequest req
= { NULL
, 0 };
2031 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
2032 if(req
.FinishedEvt
== NULL
)
2033 ERR("Failed to create event: %lu\n", GetLastError());
2036 HRESULT hr
= E_FAIL
;
2037 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
2038 hr
= WaitForResponse(&req
);
2039 if(SUCCEEDED(hr
)) switch(type
)
2041 case ALL_DEVICE_PROBE
:
2042 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
2045 case CAPTURE_DEVICE_PROBE
:
2046 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
2049 CloseHandle(req
.FinishedEvt
);
2050 req
.FinishedEvt
= NULL
;
2054 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
2056 if(type
== ALCbackend_Playback
)
2058 ALCmmdevPlayback
*backend
;
2059 NEW_OBJ(backend
, ALCmmdevPlayback
)(device
);
2060 if(!backend
) return NULL
;
2061 return STATIC_CAST(ALCbackend
, backend
);
2063 if(type
== ALCbackend_Capture
)
2065 ALCmmdevCapture
*backend
;
2066 NEW_OBJ(backend
, ALCmmdevCapture
)(device
);
2067 if(!backend
) return NULL
;
2068 return STATIC_CAST(ALCbackend
, backend
);
2075 ALCbackendFactory
*ALCmmdevBackendFactory_getFactory(void)
2077 static ALCmmdevBackendFactory factory
= ALCMMDEVBACKENDFACTORY_INITIALIZER
;
2078 return STATIC_CAST(ALCbackendFactory
, &factory
);