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 ALCwasapiProxyVtable
;
349 typedef struct ALCwasapiProxy
{
350 const struct ALCwasapiProxyVtable
*vtbl
;
353 struct ALCwasapiProxyVtable
{
354 HRESULT (*const openProxy
)(ALCwasapiProxy
*);
355 void (*const closeProxy
)(ALCwasapiProxy
*);
357 HRESULT (*const resetProxy
)(ALCwasapiProxy
*);
358 HRESULT (*const startProxy
)(ALCwasapiProxy
*);
359 void (*const stopProxy
)(ALCwasapiProxy
*);
362 #define DEFINE_ALCWASAPIPROXY_VTABLE(T) \
363 DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, openProxy) \
364 DECLARE_THUNK(T, ALCwasapiProxy, void, closeProxy) \
365 DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, resetProxy) \
366 DECLARE_THUNK(T, ALCwasapiProxy, HRESULT, startProxy) \
367 DECLARE_THUNK(T, ALCwasapiProxy, void, stopProxy) \
369 static const struct ALCwasapiProxyVtable T##_ALCwasapiProxy_vtable = { \
370 T##_ALCwasapiProxy_openProxy, \
371 T##_ALCwasapiProxy_closeProxy, \
372 T##_ALCwasapiProxy_resetProxy, \
373 T##_ALCwasapiProxy_startProxy, \
374 T##_ALCwasapiProxy_stopProxy, \
377 static void ALCwasapiProxy_Construct(ALCwasapiProxy
* UNUSED(self
)) { }
378 static void ALCwasapiProxy_Destruct(ALCwasapiProxy
* UNUSED(self
)) { }
380 static DWORD CALLBACK
ALCwasapiProxy_messageHandler(void *ptr
)
382 ThreadRequest
*req
= ptr
;
383 IMMDeviceEnumerator
*Enumerator
;
384 ALuint deviceCount
= 0;
385 ALCwasapiProxy
*proxy
;
389 TRACE("Starting message thread\n");
391 cohr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
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
= (ALCwasapiProxy
*)msg
.lParam
;
437 if(++deviceCount
== 1)
438 hr
= cohr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
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
= (ALCwasapiProxy
*)msg
.lParam
;
454 hr
= V0(proxy
,resetProxy
)();
455 ReturnMsgResponse(req
, hr
);
458 case WM_USER_StartDevice
:
459 req
= (ThreadRequest
*)msg
.wParam
;
460 proxy
= (ALCwasapiProxy
*)msg
.lParam
;
462 hr
= V0(proxy
,startProxy
)();
463 ReturnMsgResponse(req
, hr
);
466 case WM_USER_StopDevice
:
467 req
= (ThreadRequest
*)msg
.wParam
;
468 proxy
= (ALCwasapiProxy
*)msg
.lParam
;
470 V0(proxy
,stopProxy
)();
471 ReturnMsgResponse(req
, S_OK
);
474 case WM_USER_CloseDevice
:
475 req
= (ThreadRequest
*)msg
.wParam
;
476 proxy
= (ALCwasapiProxy
*)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
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
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 ALCwasapiPlayback
{
524 DERIVE_FROM_TYPE(ALCbackend
);
525 DERIVE_FROM_TYPE(ALCwasapiProxy
);
530 IAudioClient
*client
;
531 IAudioRenderClient
*render
;
536 ATOMIC(UINT32
) Padding
;
542 static int ALCwasapiPlayback_mixerProc(void *arg
);
544 static void ALCwasapiPlayback_Construct(ALCwasapiPlayback
*self
, ALCdevice
*device
);
545 static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback
*self
);
546 static ALCenum
ALCwasapiPlayback_open(ALCwasapiPlayback
*self
, const ALCchar
*name
);
547 static HRESULT
ALCwasapiPlayback_openProxy(ALCwasapiPlayback
*self
);
548 static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback
*self
);
549 static ALCboolean
ALCwasapiPlayback_reset(ALCwasapiPlayback
*self
);
550 static HRESULT
ALCwasapiPlayback_resetProxy(ALCwasapiPlayback
*self
);
551 static ALCboolean
ALCwasapiPlayback_start(ALCwasapiPlayback
*self
);
552 static HRESULT
ALCwasapiPlayback_startProxy(ALCwasapiPlayback
*self
);
553 static void ALCwasapiPlayback_stop(ALCwasapiPlayback
*self
);
554 static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback
*self
);
555 static DECLARE_FORWARD2(ALCwasapiPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
556 static DECLARE_FORWARD(ALCwasapiPlayback
, ALCbackend
, ALCuint
, availableSamples
)
557 static ClockLatency
ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback
*self
);
558 static DECLARE_FORWARD(ALCwasapiPlayback
, ALCbackend
, void, lock
)
559 static DECLARE_FORWARD(ALCwasapiPlayback
, ALCbackend
, void, unlock
)
560 DECLARE_DEFAULT_ALLOCATORS(ALCwasapiPlayback
)
562 DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiPlayback
);
563 DEFINE_ALCBACKEND_VTABLE(ALCwasapiPlayback
);
566 static void ALCwasapiPlayback_Construct(ALCwasapiPlayback
*self
, ALCdevice
*device
)
568 SET_VTABLE2(ALCwasapiPlayback
, ALCbackend
, self
);
569 SET_VTABLE2(ALCwasapiPlayback
, ALCwasapiProxy
, self
);
570 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
571 ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy
, self
));
578 self
->NotifyEvent
= NULL
;
580 self
->MsgEvent
= NULL
;
582 ATOMIC_INIT(&self
->Padding
, 0);
584 ATOMIC_INIT(&self
->killNow
, 0);
587 static void ALCwasapiPlayback_Destruct(ALCwasapiPlayback
*self
)
591 ThreadRequest req
= { self
->MsgEvent
, 0 };
592 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
593 (void)WaitForResponse(&req
);
595 CloseHandle(self
->MsgEvent
);
596 self
->MsgEvent
= NULL
;
599 if(self
->NotifyEvent
)
600 CloseHandle(self
->NotifyEvent
);
601 self
->NotifyEvent
= NULL
;
606 if(self
->NotifyEvent
!= NULL
)
607 CloseHandle(self
->NotifyEvent
);
608 self
->NotifyEvent
= NULL
;
609 if(self
->MsgEvent
!= NULL
)
610 CloseHandle(self
->MsgEvent
);
611 self
->MsgEvent
= NULL
;
616 ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy
, self
));
617 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
621 FORCE_ALIGN
static int ALCwasapiPlayback_mixerProc(void *arg
)
623 ALCwasapiPlayback
*self
= arg
;
624 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
625 UINT32 buffer_len
, written
;
626 ALuint update_size
, len
;
630 hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
633 ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr
);
634 V0(device
->Backend
,lock
)();
635 aluHandleDisconnect(device
, "COM init failed: 0x%08lx", hr
);
636 V0(device
->Backend
,unlock
)();
641 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
643 update_size
= device
->UpdateSize
;
644 buffer_len
= update_size
* device
->NumUpdates
;
645 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_relaxed
))
647 hr
= IAudioClient_GetCurrentPadding(self
->client
, &written
);
650 ERR("Failed to get padding: 0x%08lx\n", hr
);
651 V0(device
->Backend
,lock
)();
652 aluHandleDisconnect(device
, "Failed to retrieve buffer padding: 0x%08lx", hr
);
653 V0(device
->Backend
,unlock
)();
656 ATOMIC_STORE(&self
->Padding
, written
, almemory_order_relaxed
);
658 len
= buffer_len
- written
;
659 if(len
< update_size
)
662 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
663 if(res
!= WAIT_OBJECT_0
)
664 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
667 len
-= len
%update_size
;
669 hr
= IAudioRenderClient_GetBuffer(self
->render
, len
, &buffer
);
672 ALCwasapiPlayback_lock(self
);
673 aluMixData(device
, buffer
, len
);
674 ATOMIC_STORE(&self
->Padding
, written
+ len
, almemory_order_relaxed
);
675 ALCwasapiPlayback_unlock(self
);
676 hr
= IAudioRenderClient_ReleaseBuffer(self
->render
, len
, 0);
680 ERR("Failed to buffer data: 0x%08lx\n", hr
);
681 V0(device
->Backend
,lock
)();
682 aluHandleDisconnect(device
, "Failed to send playback samples: 0x%08lx", hr
);
683 V0(device
->Backend
,unlock
)();
687 ATOMIC_STORE(&self
->Padding
, 0, almemory_order_release
);
694 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
696 memset(out
, 0, sizeof(*out
));
697 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
698 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
699 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
702 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
703 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
704 if(out
->Format
.nChannels
== 1)
705 out
->dwChannelMask
= MONO
;
706 else if(out
->Format
.nChannels
== 2)
707 out
->dwChannelMask
= STEREO
;
709 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
710 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
712 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
715 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
716 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
717 if(out
->Format
.nChannels
== 1)
718 out
->dwChannelMask
= MONO
;
719 else if(out
->Format
.nChannels
== 2)
720 out
->dwChannelMask
= STEREO
;
722 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
723 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
727 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
733 static ALCenum
ALCwasapiPlayback_open(ALCwasapiPlayback
*self
, const ALCchar
*deviceName
)
737 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
738 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
739 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
741 ERR("Failed to create message events: %lu\n", GetLastError());
751 if(VECTOR_SIZE(PlaybackDevices
) == 0)
753 ThreadRequest req
= { self
->MsgEvent
, 0 };
754 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
755 (void)WaitForResponse(&req
);
759 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
760 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
761 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
763 if(iter
== VECTOR_END(PlaybackDevices
))
766 if((len
=MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, NULL
, 0)) > 0)
768 WCHAR
*wname
= calloc(sizeof(WCHAR
), len
);
769 MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, wname
, len
);
770 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
771 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
776 if(iter
== VECTOR_END(PlaybackDevices
))
777 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
780 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
781 self
->devid
= strdupW(iter
->devid
);
782 alstr_copy(&device
->DeviceName
, iter
->name
);
790 ThreadRequest req
= { self
->MsgEvent
, 0 };
793 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
794 hr
= WaitForResponse(&req
);
796 ERR("Failed to post thread message: %lu\n", GetLastError());
801 if(self
->NotifyEvent
!= NULL
)
802 CloseHandle(self
->NotifyEvent
);
803 self
->NotifyEvent
= NULL
;
804 if(self
->MsgEvent
!= NULL
)
805 CloseHandle(self
->MsgEvent
);
806 self
->MsgEvent
= NULL
;
811 ERR("Device init failed: 0x%08lx\n", hr
);
812 return ALC_INVALID_VALUE
;
818 static HRESULT
ALCwasapiPlayback_openProxy(ALCwasapiPlayback
*self
)
820 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
824 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
827 IMMDeviceEnumerator
*Enumerator
= ptr
;
829 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &self
->mmdev
);
831 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
832 IMMDeviceEnumerator_Release(Enumerator
);
836 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
840 if(alstr_empty(device
->DeviceName
))
841 get_device_name_and_guid(self
->mmdev
, &device
->DeviceName
, NULL
);
847 IMMDevice_Release(self
->mmdev
);
855 static void ALCwasapiPlayback_closeProxy(ALCwasapiPlayback
*self
)
858 IAudioClient_Release(self
->client
);
862 IMMDevice_Release(self
->mmdev
);
867 static ALCboolean
ALCwasapiPlayback_reset(ALCwasapiPlayback
*self
)
869 ThreadRequest req
= { self
->MsgEvent
, 0 };
872 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
873 hr
= WaitForResponse(&req
);
875 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
878 static HRESULT
ALCwasapiPlayback_resetProxy(ALCwasapiPlayback
*self
)
880 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
881 EndpointFormFactor formfactor
= UnknownFormFactor
;
882 WAVEFORMATEXTENSIBLE OutputType
;
883 WAVEFORMATEX
*wfx
= NULL
;
884 REFERENCE_TIME min_per
, buf_time
;
885 UINT32 buffer_len
, min_len
;
890 IAudioClient_Release(self
->client
);
893 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
896 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
901 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
904 ERR("Failed to get mix format: 0x%08lx\n", hr
);
908 if(!MakeExtensible(&OutputType
, wfx
))
916 buf_time
= ScaleCeil(device
->UpdateSize
*device
->NumUpdates
, REFTIME_PER_SEC
,
919 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
920 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
921 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
923 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
924 device
->FmtChans
= DevFmtMono
;
925 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
926 device
->FmtChans
= DevFmtStereo
;
927 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
928 device
->FmtChans
= DevFmtQuad
;
929 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
930 device
->FmtChans
= DevFmtX51
;
931 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
932 device
->FmtChans
= DevFmtX51Rear
;
933 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
934 device
->FmtChans
= DevFmtX61
;
935 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
936 device
->FmtChans
= DevFmtX71
;
938 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
941 switch(device
->FmtChans
)
944 OutputType
.Format
.nChannels
= 1;
945 OutputType
.dwChannelMask
= MONO
;
948 device
->FmtChans
= DevFmtStereo
;
951 OutputType
.Format
.nChannels
= 2;
952 OutputType
.dwChannelMask
= STEREO
;
955 OutputType
.Format
.nChannels
= 4;
956 OutputType
.dwChannelMask
= QUAD
;
959 OutputType
.Format
.nChannels
= 6;
960 OutputType
.dwChannelMask
= X5DOT1
;
963 OutputType
.Format
.nChannels
= 6;
964 OutputType
.dwChannelMask
= X5DOT1REAR
;
967 OutputType
.Format
.nChannels
= 7;
968 OutputType
.dwChannelMask
= X6DOT1
;
971 OutputType
.Format
.nChannels
= 8;
972 OutputType
.dwChannelMask
= X7DOT1
;
975 switch(device
->FmtType
)
978 device
->FmtType
= DevFmtUByte
;
981 OutputType
.Format
.wBitsPerSample
= 8;
982 OutputType
.Samples
.wValidBitsPerSample
= 8;
983 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
986 device
->FmtType
= DevFmtShort
;
989 OutputType
.Format
.wBitsPerSample
= 16;
990 OutputType
.Samples
.wValidBitsPerSample
= 16;
991 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
994 device
->FmtType
= DevFmtInt
;
997 OutputType
.Format
.wBitsPerSample
= 32;
998 OutputType
.Samples
.wValidBitsPerSample
= 32;
999 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1002 OutputType
.Format
.wBitsPerSample
= 32;
1003 OutputType
.Samples
.wValidBitsPerSample
= 32;
1004 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1007 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1009 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1010 OutputType
.Format
.wBitsPerSample
/ 8;
1011 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1012 OutputType
.Format
.nBlockAlign
;
1014 hr
= IAudioClient_IsFormatSupported(self
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
1017 ERR("Failed to check format support: 0x%08lx\n", hr
);
1018 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
1022 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
1028 if(!MakeExtensible(&OutputType
, wfx
))
1036 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
1037 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
1038 device
->FmtChans
= DevFmtMono
;
1039 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
1040 device
->FmtChans
= DevFmtStereo
;
1041 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
1042 device
->FmtChans
= DevFmtQuad
;
1043 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
1044 device
->FmtChans
= DevFmtX51
;
1045 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
1046 device
->FmtChans
= DevFmtX51Rear
;
1047 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
1048 device
->FmtChans
= DevFmtX61
;
1049 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
1050 device
->FmtChans
= DevFmtX71
;
1053 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
1054 device
->FmtChans
= DevFmtStereo
;
1055 OutputType
.Format
.nChannels
= 2;
1056 OutputType
.dwChannelMask
= STEREO
;
1059 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
1061 if(OutputType
.Format
.wBitsPerSample
== 8)
1062 device
->FmtType
= DevFmtUByte
;
1063 else if(OutputType
.Format
.wBitsPerSample
== 16)
1064 device
->FmtType
= DevFmtShort
;
1065 else if(OutputType
.Format
.wBitsPerSample
== 32)
1066 device
->FmtType
= DevFmtInt
;
1069 device
->FmtType
= DevFmtShort
;
1070 OutputType
.Format
.wBitsPerSample
= 16;
1073 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
1075 device
->FmtType
= DevFmtFloat
;
1076 OutputType
.Format
.wBitsPerSample
= 32;
1080 ERR("Unhandled format sub-type\n");
1081 device
->FmtType
= DevFmtShort
;
1082 OutputType
.Format
.wBitsPerSample
= 16;
1083 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1085 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
1087 get_device_formfactor(self
->mmdev
, &formfactor
);
1088 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&&
1089 (formfactor
== Headphones
|| formfactor
== Headset
)
1092 SetDefaultWFXChannelOrder(device
);
1094 hr
= IAudioClient_Initialize(self
->client
, AUDCLNT_SHAREMODE_SHARED
,
1095 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1096 buf_time
, 0, &OutputType
.Format
, NULL
);
1099 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1103 hr
= IAudioClient_GetDevicePeriod(self
->client
, &min_per
, NULL
);
1106 min_len
= (UINT32
)ScaleCeil(min_per
, device
->Frequency
, REFTIME_PER_SEC
);
1107 /* Find the nearest multiple of the period size to the update size */
1108 if(min_len
< device
->UpdateSize
)
1109 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
1110 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1114 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
1118 device
->UpdateSize
= min_len
;
1119 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
1120 if(device
->NumUpdates
<= 1)
1122 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1123 device
->NumUpdates
= 2;
1124 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
1127 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1130 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1138 static ALCboolean
ALCwasapiPlayback_start(ALCwasapiPlayback
*self
)
1140 ThreadRequest req
= { self
->MsgEvent
, 0 };
1141 HRESULT hr
= E_FAIL
;
1143 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
1144 hr
= WaitForResponse(&req
);
1146 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1149 static HRESULT
ALCwasapiPlayback_startProxy(ALCwasapiPlayback
*self
)
1154 ResetEvent(self
->NotifyEvent
);
1155 hr
= IAudioClient_Start(self
->client
);
1157 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1160 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioRenderClient
, &ptr
);
1164 ATOMIC_STORE(&self
->killNow
, 0, almemory_order_release
);
1165 if(althrd_create(&self
->thread
, ALCwasapiPlayback_mixerProc
, self
) != althrd_success
)
1168 IAudioRenderClient_Release(self
->render
);
1169 self
->render
= NULL
;
1170 IAudioClient_Stop(self
->client
);
1171 ERR("Failed to start thread\n");
1180 static void ALCwasapiPlayback_stop(ALCwasapiPlayback
*self
)
1182 ThreadRequest req
= { self
->MsgEvent
, 0 };
1183 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
1184 (void)WaitForResponse(&req
);
1187 static void ALCwasapiPlayback_stopProxy(ALCwasapiPlayback
*self
)
1194 ATOMIC_STORE_SEQ(&self
->killNow
, 1);
1195 althrd_join(self
->thread
, &res
);
1197 IAudioRenderClient_Release(self
->render
);
1198 self
->render
= NULL
;
1199 IAudioClient_Stop(self
->client
);
1203 static ClockLatency
ALCwasapiPlayback_getClockLatency(ALCwasapiPlayback
*self
)
1205 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1208 ALCwasapiPlayback_lock(self
);
1209 ret
.ClockTime
= GetDeviceClockTime(device
);
1210 ret
.Latency
= ATOMIC_LOAD(&self
->Padding
, almemory_order_relaxed
) * DEVICE_CLOCK_RES
/
1212 ALCwasapiPlayback_unlock(self
);
1218 typedef struct ALCwasapiCapture
{
1219 DERIVE_FROM_TYPE(ALCbackend
);
1220 DERIVE_FROM_TYPE(ALCwasapiProxy
);
1225 IAudioClient
*client
;
1226 IAudioCaptureClient
*capture
;
1231 ChannelConverter
*ChannelConv
;
1232 SampleConverter
*SampleConv
;
1233 ll_ringbuffer_t
*Ring
;
1235 ATOMIC(int) killNow
;
1239 static int ALCwasapiCapture_recordProc(void *arg
);
1241 static void ALCwasapiCapture_Construct(ALCwasapiCapture
*self
, ALCdevice
*device
);
1242 static void ALCwasapiCapture_Destruct(ALCwasapiCapture
*self
);
1243 static ALCenum
ALCwasapiCapture_open(ALCwasapiCapture
*self
, const ALCchar
*name
);
1244 static HRESULT
ALCwasapiCapture_openProxy(ALCwasapiCapture
*self
);
1245 static void ALCwasapiCapture_closeProxy(ALCwasapiCapture
*self
);
1246 static DECLARE_FORWARD(ALCwasapiCapture
, ALCbackend
, ALCboolean
, reset
)
1247 static HRESULT
ALCwasapiCapture_resetProxy(ALCwasapiCapture
*self
);
1248 static ALCboolean
ALCwasapiCapture_start(ALCwasapiCapture
*self
);
1249 static HRESULT
ALCwasapiCapture_startProxy(ALCwasapiCapture
*self
);
1250 static void ALCwasapiCapture_stop(ALCwasapiCapture
*self
);
1251 static void ALCwasapiCapture_stopProxy(ALCwasapiCapture
*self
);
1252 static ALCenum
ALCwasapiCapture_captureSamples(ALCwasapiCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
1253 static ALuint
ALCwasapiCapture_availableSamples(ALCwasapiCapture
*self
);
1254 static DECLARE_FORWARD(ALCwasapiCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
1255 static DECLARE_FORWARD(ALCwasapiCapture
, ALCbackend
, void, lock
)
1256 static DECLARE_FORWARD(ALCwasapiCapture
, ALCbackend
, void, unlock
)
1257 DECLARE_DEFAULT_ALLOCATORS(ALCwasapiCapture
)
1259 DEFINE_ALCWASAPIPROXY_VTABLE(ALCwasapiCapture
);
1260 DEFINE_ALCBACKEND_VTABLE(ALCwasapiCapture
);
1263 static void ALCwasapiCapture_Construct(ALCwasapiCapture
*self
, ALCdevice
*device
)
1265 SET_VTABLE2(ALCwasapiCapture
, ALCbackend
, self
);
1266 SET_VTABLE2(ALCwasapiCapture
, ALCwasapiProxy
, self
);
1267 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
1268 ALCwasapiProxy_Construct(STATIC_CAST(ALCwasapiProxy
, self
));
1273 self
->client
= NULL
;
1274 self
->capture
= NULL
;
1275 self
->NotifyEvent
= NULL
;
1277 self
->MsgEvent
= NULL
;
1279 self
->ChannelConv
= NULL
;
1280 self
->SampleConv
= NULL
;
1283 ATOMIC_INIT(&self
->killNow
, 0);
1286 static void ALCwasapiCapture_Destruct(ALCwasapiCapture
*self
)
1290 ThreadRequest req
= { self
->MsgEvent
, 0 };
1291 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
1292 (void)WaitForResponse(&req
);
1294 CloseHandle(self
->MsgEvent
);
1295 self
->MsgEvent
= NULL
;
1298 if(self
->NotifyEvent
!= NULL
)
1299 CloseHandle(self
->NotifyEvent
);
1300 self
->NotifyEvent
= NULL
;
1302 ll_ringbuffer_free(self
->Ring
);
1305 DestroySampleConverter(&self
->SampleConv
);
1306 DestroyChannelConverter(&self
->ChannelConv
);
1311 ALCwasapiProxy_Destruct(STATIC_CAST(ALCwasapiProxy
, self
));
1312 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
1316 FORCE_ALIGN
int ALCwasapiCapture_recordProc(void *arg
)
1318 ALCwasapiCapture
*self
= arg
;
1319 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1320 ALfloat
*samples
= NULL
;
1321 size_t samplesmax
= 0;
1324 hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
1327 ERR("CoInitializeEx(NULL, COINIT_MULTITHREADED) failed: 0x%08lx\n", hr
);
1328 V0(device
->Backend
,lock
)();
1329 aluHandleDisconnect(device
, "COM init failed: 0x%08lx", hr
);
1330 V0(device
->Backend
,unlock
)();
1334 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
1336 while(!ATOMIC_LOAD(&self
->killNow
, almemory_order_relaxed
))
1341 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1343 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1350 hr
= IAudioCaptureClient_GetBuffer(self
->capture
,
1351 &rdata
, &numsamples
, &flags
, NULL
, NULL
1354 ERR("Failed to get capture buffer: 0x%08lx\n", hr
);
1357 ll_ringbuffer_data_t data
[2];
1358 size_t dstframes
= 0;
1360 if(self
->ChannelConv
)
1362 if(samplesmax
< numsamples
)
1364 size_t newmax
= RoundUp(numsamples
, 4096);
1365 ALfloat
*tmp
= al_calloc(DEF_ALIGN
, newmax
*2*sizeof(ALfloat
));
1368 samplesmax
= newmax
;
1370 ChannelConverterInput(self
->ChannelConv
, rdata
, samples
, numsamples
);
1371 rdata
= (BYTE
*)samples
;
1374 ll_ringbuffer_get_write_vector(self
->Ring
, data
);
1376 if(self
->SampleConv
)
1378 const ALvoid
*srcdata
= rdata
;
1379 ALsizei srcframes
= numsamples
;
1381 dstframes
= SampleConverterInput(self
->SampleConv
,
1382 &srcdata
, &srcframes
, data
[0].buf
, (ALsizei
)minz(data
[0].len
, INT_MAX
)
1384 if(srcframes
> 0 && dstframes
== data
[0].len
&& data
[1].len
> 0)
1386 /* If some source samples remain, all of the first dest
1387 * block was filled, and there's space in the second
1388 * dest block, do another run for the second block.
1390 dstframes
+= SampleConverterInput(self
->SampleConv
,
1391 &srcdata
, &srcframes
, data
[1].buf
, (ALsizei
)minz(data
[1].len
, INT_MAX
)
1397 ALuint framesize
= FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
,
1399 size_t len1
= minz(data
[0].len
, numsamples
);
1400 size_t len2
= minz(data
[1].len
, numsamples
-len1
);
1402 memcpy(data
[0].buf
, rdata
, len1
*framesize
);
1404 memcpy(data
[1].buf
, rdata
+len1
*framesize
, len2
*framesize
);
1405 dstframes
= len1
+ len2
;
1408 ll_ringbuffer_write_advance(self
->Ring
, dstframes
);
1410 hr
= IAudioCaptureClient_ReleaseBuffer(self
->capture
, numsamples
);
1411 if(FAILED(hr
)) ERR("Failed to release capture buffer: 0x%08lx\n", hr
);
1417 V0(device
->Backend
,lock
)();
1418 aluHandleDisconnect(device
, "Failed to capture samples: 0x%08lx", hr
);
1419 V0(device
->Backend
,unlock
)();
1423 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
1424 if(res
!= WAIT_OBJECT_0
)
1425 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
1437 static ALCenum
ALCwasapiCapture_open(ALCwasapiCapture
*self
, const ALCchar
*deviceName
)
1441 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1442 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1443 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
1445 ERR("Failed to create message events: %lu\n", GetLastError());
1455 if(VECTOR_SIZE(CaptureDevices
) == 0)
1457 ThreadRequest req
= { self
->MsgEvent
, 0 };
1458 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, CAPTURE_DEVICE_PROBE
))
1459 (void)WaitForResponse(&req
);
1463 #define MATCH_NAME(i) (alstr_cmp_cstr((i)->name, deviceName) == 0 || \
1464 alstr_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
1465 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1467 if(iter
== VECTOR_END(CaptureDevices
))
1470 if((len
=MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, NULL
, 0)) > 0)
1472 WCHAR
*wname
= calloc(sizeof(WCHAR
), len
);
1473 MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, wname
, len
);
1474 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
1475 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1480 if(iter
== VECTOR_END(CaptureDevices
))
1481 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
1484 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
1485 self
->devid
= strdupW(iter
->devid
);
1486 alstr_copy(&device
->DeviceName
, iter
->name
);
1494 ThreadRequest req
= { self
->MsgEvent
, 0 };
1497 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
1498 hr
= WaitForResponse(&req
);
1500 ERR("Failed to post thread message: %lu\n", GetLastError());
1505 if(self
->NotifyEvent
!= NULL
)
1506 CloseHandle(self
->NotifyEvent
);
1507 self
->NotifyEvent
= NULL
;
1508 if(self
->MsgEvent
!= NULL
)
1509 CloseHandle(self
->MsgEvent
);
1510 self
->MsgEvent
= NULL
;
1515 ERR("Device init failed: 0x%08lx\n", hr
);
1516 return ALC_INVALID_VALUE
;
1520 ThreadRequest req
= { self
->MsgEvent
, 0 };
1523 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
1524 hr
= WaitForResponse(&req
);
1526 ERR("Failed to post thread message: %lu\n", GetLastError());
1530 if(hr
== E_OUTOFMEMORY
)
1531 return ALC_OUT_OF_MEMORY
;
1532 return ALC_INVALID_VALUE
;
1536 return ALC_NO_ERROR
;
1539 static HRESULT
ALCwasapiCapture_openProxy(ALCwasapiCapture
*self
)
1541 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1545 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
1548 IMMDeviceEnumerator
*Enumerator
= ptr
;
1550 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eCapture
, eMultimedia
, &self
->mmdev
);
1552 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
1553 IMMDeviceEnumerator_Release(Enumerator
);
1557 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1561 if(alstr_empty(device
->DeviceName
))
1562 get_device_name_and_guid(self
->mmdev
, &device
->DeviceName
, NULL
);
1568 IMMDevice_Release(self
->mmdev
);
1576 static void ALCwasapiCapture_closeProxy(ALCwasapiCapture
*self
)
1579 IAudioClient_Release(self
->client
);
1580 self
->client
= NULL
;
1583 IMMDevice_Release(self
->mmdev
);
1588 static HRESULT
ALCwasapiCapture_resetProxy(ALCwasapiCapture
*self
)
1590 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1591 WAVEFORMATEXTENSIBLE OutputType
;
1592 WAVEFORMATEX
*wfx
= NULL
;
1593 enum DevFmtType srcType
;
1594 REFERENCE_TIME buf_time
;
1600 IAudioClient_Release(self
->client
);
1601 self
->client
= NULL
;
1603 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1606 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
1611 buf_time
= ScaleCeil(device
->UpdateSize
*device
->NumUpdates
, REFTIME_PER_SEC
,
1613 // Make sure buffer is at least 100ms in size
1614 buf_time
= maxu64(buf_time
, REFTIME_PER_SEC
/10);
1615 device
->UpdateSize
= (ALuint
)ScaleCeil(buf_time
, device
->Frequency
, REFTIME_PER_SEC
) /
1618 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1619 switch(device
->FmtChans
)
1622 OutputType
.Format
.nChannels
= 1;
1623 OutputType
.dwChannelMask
= MONO
;
1626 OutputType
.Format
.nChannels
= 2;
1627 OutputType
.dwChannelMask
= STEREO
;
1630 OutputType
.Format
.nChannels
= 4;
1631 OutputType
.dwChannelMask
= QUAD
;
1634 OutputType
.Format
.nChannels
= 6;
1635 OutputType
.dwChannelMask
= X5DOT1
;
1638 OutputType
.Format
.nChannels
= 6;
1639 OutputType
.dwChannelMask
= X5DOT1REAR
;
1642 OutputType
.Format
.nChannels
= 7;
1643 OutputType
.dwChannelMask
= X6DOT1
;
1646 OutputType
.Format
.nChannels
= 8;
1647 OutputType
.dwChannelMask
= X7DOT1
;
1653 switch(device
->FmtType
)
1655 /* NOTE: Signedness doesn't matter, the converter will handle it. */
1658 OutputType
.Format
.wBitsPerSample
= 8;
1659 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1663 OutputType
.Format
.wBitsPerSample
= 16;
1664 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1668 OutputType
.Format
.wBitsPerSample
= 32;
1669 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1672 OutputType
.Format
.wBitsPerSample
= 32;
1673 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1676 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
1677 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1679 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1680 OutputType
.Format
.wBitsPerSample
/ 8;
1681 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1682 OutputType
.Format
.nBlockAlign
;
1683 OutputType
.Format
.cbSize
= sizeof(OutputType
) - sizeof(OutputType
.Format
);
1685 hr
= IAudioClient_IsFormatSupported(self
->client
,
1686 AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
1690 ERR("Failed to check format support: 0x%08lx\n", hr
);
1694 DestroySampleConverter(&self
->SampleConv
);
1695 DestroyChannelConverter(&self
->ChannelConv
);
1699 if(!(wfx
->nChannels
== OutputType
.Format
.nChannels
||
1700 (wfx
->nChannels
== 1 && OutputType
.Format
.nChannels
== 2) ||
1701 (wfx
->nChannels
== 2 && OutputType
.Format
.nChannels
== 1)))
1703 ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
1704 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1705 device
->Frequency
, wfx
->nChannels
, (wfx
->nChannels
==1)?"":"s", wfx
->wBitsPerSample
,
1706 wfx
->nSamplesPerSec
);
1711 if(!MakeExtensible(&OutputType
, wfx
))
1720 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
1722 if(OutputType
.Format
.wBitsPerSample
== 8)
1723 srcType
= DevFmtUByte
;
1724 else if(OutputType
.Format
.wBitsPerSample
== 16)
1725 srcType
= DevFmtShort
;
1726 else if(OutputType
.Format
.wBitsPerSample
== 32)
1727 srcType
= DevFmtInt
;
1730 ERR("Unhandled integer bit depth: %d\n", OutputType
.Format
.wBitsPerSample
);
1734 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
1736 if(OutputType
.Format
.wBitsPerSample
== 32)
1737 srcType
= DevFmtFloat
;
1740 ERR("Unhandled float bit depth: %d\n", OutputType
.Format
.wBitsPerSample
);
1746 ERR("Unhandled format sub-type\n");
1750 if(device
->FmtChans
== DevFmtMono
&& OutputType
.Format
.nChannels
== 2)
1752 self
->ChannelConv
= CreateChannelConverter(srcType
, DevFmtStereo
,
1754 if(!self
->ChannelConv
)
1756 ERR("Failed to create %s stereo-to-mono converter\n", DevFmtTypeString(srcType
));
1759 TRACE("Created %s stereo-to-mono converter\n", DevFmtTypeString(srcType
));
1760 /* The channel converter always outputs float, so change the input type
1761 * for the resampler/type-converter.
1763 srcType
= DevFmtFloat
;
1765 else if(device
->FmtChans
== DevFmtStereo
&& OutputType
.Format
.nChannels
== 1)
1767 self
->ChannelConv
= CreateChannelConverter(srcType
, DevFmtMono
,
1769 if(!self
->ChannelConv
)
1771 ERR("Failed to create %s mono-to-stereo converter\n", DevFmtTypeString(srcType
));
1774 TRACE("Created %s mono-to-stereo converter\n", DevFmtTypeString(srcType
));
1775 srcType
= DevFmtFloat
;
1778 if(device
->Frequency
!= OutputType
.Format
.nSamplesPerSec
|| device
->FmtType
!= srcType
)
1780 self
->SampleConv
= CreateSampleConverter(
1781 srcType
, device
->FmtType
, ChannelsFromDevFmt(device
->FmtChans
, device
->AmbiOrder
),
1782 OutputType
.Format
.nSamplesPerSec
, device
->Frequency
1784 if(!self
->SampleConv
)
1786 ERR("Failed to create converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1787 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1788 device
->Frequency
, DevFmtTypeString(srcType
), OutputType
.Format
.nSamplesPerSec
);
1791 TRACE("Created converter for %s format, dst: %s %uhz, src: %s %luhz\n",
1792 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1793 device
->Frequency
, DevFmtTypeString(srcType
), OutputType
.Format
.nSamplesPerSec
);
1796 hr
= IAudioClient_Initialize(self
->client
,
1797 AUDCLNT_SHAREMODE_SHARED
, AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1798 buf_time
, 0, &OutputType
.Format
, NULL
1802 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1806 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1809 ERR("Failed to get buffer size: 0x%08lx\n", hr
);
1813 buffer_len
= maxu(device
->UpdateSize
*device
->NumUpdates
, buffer_len
);
1814 ll_ringbuffer_free(self
->Ring
);
1815 self
->Ring
= ll_ringbuffer_create(buffer_len
,
1816 FrameSizeFromDevFmt(device
->FmtChans
, device
->FmtType
, device
->AmbiOrder
),
1821 ERR("Failed to allocate capture ring buffer\n");
1822 return E_OUTOFMEMORY
;
1825 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1828 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1836 static ALCboolean
ALCwasapiCapture_start(ALCwasapiCapture
*self
)
1838 ThreadRequest req
= { self
->MsgEvent
, 0 };
1839 HRESULT hr
= E_FAIL
;
1841 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
1842 hr
= WaitForResponse(&req
);
1844 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1847 static HRESULT
ALCwasapiCapture_startProxy(ALCwasapiCapture
*self
)
1852 ResetEvent(self
->NotifyEvent
);
1853 hr
= IAudioClient_Start(self
->client
);
1856 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1860 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioCaptureClient
, &ptr
);
1863 self
->capture
= ptr
;
1864 ATOMIC_STORE(&self
->killNow
, 0, almemory_order_release
);
1865 if(althrd_create(&self
->thread
, ALCwasapiCapture_recordProc
, self
) != althrd_success
)
1867 ERR("Failed to start thread\n");
1868 IAudioCaptureClient_Release(self
->capture
);
1869 self
->capture
= NULL
;
1876 IAudioClient_Stop(self
->client
);
1877 IAudioClient_Reset(self
->client
);
1884 static void ALCwasapiCapture_stop(ALCwasapiCapture
*self
)
1886 ThreadRequest req
= { self
->MsgEvent
, 0 };
1887 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCwasapiProxy
, self
)))
1888 (void)WaitForResponse(&req
);
1891 static void ALCwasapiCapture_stopProxy(ALCwasapiCapture
*self
)
1898 ATOMIC_STORE_SEQ(&self
->killNow
, 1);
1899 althrd_join(self
->thread
, &res
);
1901 IAudioCaptureClient_Release(self
->capture
);
1902 self
->capture
= NULL
;
1903 IAudioClient_Stop(self
->client
);
1904 IAudioClient_Reset(self
->client
);
1908 ALuint
ALCwasapiCapture_availableSamples(ALCwasapiCapture
*self
)
1910 return (ALuint
)ll_ringbuffer_read_space(self
->Ring
);
1913 ALCenum
ALCwasapiCapture_captureSamples(ALCwasapiCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
1915 if(ALCwasapiCapture_availableSamples(self
) < samples
)
1916 return ALC_INVALID_VALUE
;
1917 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
1918 return ALC_NO_ERROR
;
1922 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1923 { AppendAllDevicesList(alstr_get_cstr(entry
->name
)); }
1924 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1925 { AppendCaptureDeviceList(alstr_get_cstr(entry
->name
)); }
1927 typedef struct ALCwasapiBackendFactory
{
1928 DERIVE_FROM_TYPE(ALCbackendFactory
);
1929 } ALCwasapiBackendFactory
;
1930 #define ALCWASAPIBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCwasapiBackendFactory, ALCbackendFactory) } }
1932 static ALCboolean
ALCwasapiBackendFactory_init(ALCwasapiBackendFactory
*self
);
1933 static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory
*self
);
1934 static ALCboolean
ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory
*self
, ALCbackend_Type type
);
1935 static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory
*self
, enum DevProbe type
);
1936 static ALCbackend
* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
1938 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCwasapiBackendFactory
);
1941 static ALCboolean
ALCwasapiBackendFactory_init(ALCwasapiBackendFactory
* UNUSED(self
))
1943 static HRESULT InitResult
;
1945 VECTOR_INIT(PlaybackDevices
);
1946 VECTOR_INIT(CaptureDevices
);
1951 InitResult
= E_FAIL
;
1953 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1954 if(req
.FinishedEvt
== NULL
)
1955 ERR("Failed to create event: %lu\n", GetLastError());
1958 ThreadHdl
= CreateThread(NULL
, 0, ALCwasapiProxy_messageHandler
, &req
, 0, &ThreadID
);
1959 if(ThreadHdl
!= NULL
)
1960 InitResult
= WaitForResponse(&req
);
1961 CloseHandle(req
.FinishedEvt
);
1965 return SUCCEEDED(InitResult
) ? ALC_TRUE
: ALC_FALSE
;
1968 static void ALCwasapiBackendFactory_deinit(ALCwasapiBackendFactory
* UNUSED(self
))
1970 clear_devlist(&PlaybackDevices
);
1971 VECTOR_DEINIT(PlaybackDevices
);
1973 clear_devlist(&CaptureDevices
);
1974 VECTOR_DEINIT(CaptureDevices
);
1978 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
1979 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
1980 CloseHandle(ThreadHdl
);
1985 static ALCboolean
ALCwasapiBackendFactory_querySupport(ALCwasapiBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1987 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
1992 static void ALCwasapiBackendFactory_probe(ALCwasapiBackendFactory
* UNUSED(self
), enum DevProbe type
)
1994 ThreadRequest req
= { NULL
, 0 };
1996 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1997 if(req
.FinishedEvt
== NULL
)
1998 ERR("Failed to create event: %lu\n", GetLastError());
2001 HRESULT hr
= E_FAIL
;
2002 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
2003 hr
= WaitForResponse(&req
);
2004 if(SUCCEEDED(hr
)) switch(type
)
2006 case ALL_DEVICE_PROBE
:
2007 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
2010 case CAPTURE_DEVICE_PROBE
:
2011 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
2014 CloseHandle(req
.FinishedEvt
);
2015 req
.FinishedEvt
= NULL
;
2019 static ALCbackend
* ALCwasapiBackendFactory_createBackend(ALCwasapiBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
2021 if(type
== ALCbackend_Playback
)
2023 ALCwasapiPlayback
*backend
;
2024 NEW_OBJ(backend
, ALCwasapiPlayback
)(device
);
2025 if(!backend
) return NULL
;
2026 return STATIC_CAST(ALCbackend
, backend
);
2028 if(type
== ALCbackend_Capture
)
2030 ALCwasapiCapture
*backend
;
2031 NEW_OBJ(backend
, ALCwasapiCapture
)(device
);
2032 if(!backend
) return NULL
;
2033 return STATIC_CAST(ALCbackend
, backend
);
2040 ALCbackendFactory
*ALCwasapiBackendFactory_getFactory(void)
2042 static ALCwasapiBackendFactory factory
= ALCWASAPIBACKENDFACTORY_INITIALIZER
;
2043 return STATIC_CAST(ALCbackendFactory
, &factory
);