2 * OpenAL cross platform audio library
3 * Copyright (C) 2011 by authors.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 * Or go to http://www.gnu.org/copyleft/lgpl.html
28 #include <mmdeviceapi.h>
29 #include <audioclient.h>
31 #include <devpropdef.h>
36 #ifndef _WAVEFORMATEXTENSIBLE_
47 #include "backends/base.h"
50 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
51 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
53 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName
, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
54 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor
, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
56 #define MONO SPEAKER_FRONT_CENTER
57 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
58 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
59 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
60 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
61 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
62 #define X7DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
63 #define X7DOT1_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)
65 #define DEVNAME_HEAD "OpenAL Soft on "
72 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
74 static void clear_devlist(vector_DevMap
*list
)
76 #define CLEAR_DEVMAP(i) do { \
77 AL_STRING_DEINIT((i)->name); \
81 VECTOR_FOR_EACH(DevMap
, *list
, CLEAR_DEVMAP
);
82 VECTOR_RESIZE(*list
, 0, 0);
86 static vector_DevMap PlaybackDevices
;
87 static vector_DevMap CaptureDevices
;
90 static HANDLE ThreadHdl
;
91 static DWORD ThreadID
;
98 #define WM_USER_First (WM_USER+0)
99 #define WM_USER_OpenDevice (WM_USER+0)
100 #define WM_USER_ResetDevice (WM_USER+1)
101 #define WM_USER_StartDevice (WM_USER+2)
102 #define WM_USER_StopDevice (WM_USER+3)
103 #define WM_USER_CloseDevice (WM_USER+4)
104 #define WM_USER_Enumerate (WM_USER+5)
105 #define WM_USER_Last (WM_USER+5)
107 static inline void ReturnMsgResponse(ThreadRequest
*req
, HRESULT res
)
110 SetEvent(req
->FinishedEvt
);
113 static HRESULT
WaitForResponse(ThreadRequest
*req
)
115 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
117 ERR("Message response error: %lu\n", GetLastError());
122 static void get_device_name(IMMDevice
*device
, al_string
*name
)
128 al_string_copy_cstr(name
, DEVNAME_HEAD
);
130 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
133 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
134 al_string_append_cstr(name
, "Unknown Device Name");
138 PropVariantInit(&pvname
);
140 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
143 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr
);
144 al_string_append_cstr(name
, "Unknown Device Name");
146 else if(pvname
.vt
== VT_LPWSTR
)
147 al_string_append_wcstr(name
, pvname
.pwszVal
);
150 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname
.vt
);
151 al_string_append_cstr(name
, "Unknown Device Name");
154 PropVariantClear(&pvname
);
155 IPropertyStore_Release(ps
);
158 static void get_device_formfactor(IMMDevice
*device
, EndpointFormFactor
*formfactor
)
164 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
167 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
171 PropVariantInit(&pvform
);
173 hr
= IPropertyStore_GetValue(ps
, &PKEY_AudioEndpoint_FormFactor
, &pvform
);
175 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr
);
176 else if(pvform
.vt
== VT_UI4
)
177 *formfactor
= pvform
.ulVal
;
178 else if(pvform
.vt
== VT_EMPTY
)
179 *formfactor
= UnknownFormFactor
;
181 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform
.vt
);
183 PropVariantClear(&pvform
);
184 IPropertyStore_Release(ps
);
188 static void add_device(IMMDevice
*device
, LPCWSTR devid
, vector_DevMap
*list
)
194 AL_STRING_INIT(tmpname
);
195 AL_STRING_INIT(entry
.name
);
197 entry
.devid
= strdupW(devid
);
198 get_device_name(device
, &tmpname
);
204 al_string_copy(&entry
.name
, tmpname
);
208 snprintf(str
, sizeof(str
), " #%d", count
+1);
209 al_string_append_cstr(&entry
.name
, str
);
212 #define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
213 VECTOR_FIND_IF(iter
, const DevMap
, *list
, MATCH_ENTRY
);
214 if(iter
== VECTOR_END(*list
)) break;
219 TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry
.name
), entry
.devid
);
220 VECTOR_PUSH_BACK(*list
, entry
);
222 AL_STRING_DEINIT(tmpname
);
225 static LPWSTR
get_device_id(IMMDevice
*device
)
230 hr
= IMMDevice_GetId(device
, &devid
);
233 ERR("Failed to get device id: %lx\n", hr
);
240 static HRESULT
probe_devices(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, vector_DevMap
*list
)
242 IMMDeviceCollection
*coll
;
243 IMMDevice
*defdev
= NULL
;
244 LPWSTR defdevid
= NULL
;
249 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
252 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
257 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
258 if(SUCCEEDED(hr
) && count
> 0)
261 VECTOR_RESIZE(*list
, 0, count
);
263 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, flowdir
,
264 eMultimedia
, &defdev
);
266 if(SUCCEEDED(hr
) && defdev
!= NULL
)
268 defdevid
= get_device_id(defdev
);
270 add_device(defdev
, defdevid
, list
);
273 for(i
= 0;i
< count
;++i
)
278 hr
= IMMDeviceCollection_Item(coll
, i
, &device
);
279 if(FAILED(hr
)) continue;
281 devid
= get_device_id(device
);
284 if(wcscmp(devid
, defdevid
) != 0)
285 add_device(device
, devid
, list
);
286 CoTaskMemFree(devid
);
288 IMMDevice_Release(device
);
291 if(defdev
) IMMDevice_Release(defdev
);
292 if(defdevid
) CoTaskMemFree(defdevid
);
293 IMMDeviceCollection_Release(coll
);
299 /* Proxy interface used by the message handler. */
300 struct ALCmmdevProxyVtable
;
302 typedef struct ALCmmdevProxy
{
303 const struct ALCmmdevProxyVtable
*vtbl
;
306 struct ALCmmdevProxyVtable
{
307 HRESULT (*const openProxy
)(ALCmmdevProxy
*);
308 void (*const closeProxy
)(ALCmmdevProxy
*);
310 HRESULT (*const resetProxy
)(ALCmmdevProxy
*);
311 HRESULT (*const startProxy
)(ALCmmdevProxy
*);
312 void (*const stopProxy
)(ALCmmdevProxy
*);
315 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
316 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
317 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
318 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
319 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
320 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
322 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
323 T##_ALCmmdevProxy_openProxy, \
324 T##_ALCmmdevProxy_closeProxy, \
325 T##_ALCmmdevProxy_resetProxy, \
326 T##_ALCmmdevProxy_startProxy, \
327 T##_ALCmmdevProxy_stopProxy, \
330 static void ALCmmdevProxy_Construct(ALCmmdevProxy
* UNUSED(self
)) { }
331 static void ALCmmdevProxy_Destruct(ALCmmdevProxy
* UNUSED(self
)) { }
333 static DWORD CALLBACK
ALCmmdevProxy_messageHandler(void *ptr
)
335 ThreadRequest
*req
= ptr
;
336 IMMDeviceEnumerator
*Enumerator
;
337 ALuint deviceCount
= 0;
338 ALCmmdevProxy
*proxy
;
342 TRACE("Starting message thread\n");
344 cohr
= CoInitialize(NULL
);
347 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
348 ReturnMsgResponse(req
, cohr
);
352 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
355 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
357 ReturnMsgResponse(req
, hr
);
361 IMMDeviceEnumerator_Release(Enumerator
);
366 /* HACK: Force Windows to create a message queue for this thread before
367 * returning success, otherwise PostThreadMessage may fail if it gets
368 * called before GetMessage.
370 PeekMessage(&msg
, NULL
, WM_USER
, WM_USER
, PM_NOREMOVE
);
372 TRACE("Message thread initialization complete\n");
373 ReturnMsgResponse(req
, S_OK
);
375 TRACE("Starting message loop\n");
376 while(GetMessage(&msg
, NULL
, WM_USER_First
, WM_USER_Last
))
378 TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg
.message
, (void*)msg
.lParam
, (void*)msg
.wParam
);
381 case WM_USER_OpenDevice
:
382 req
= (ThreadRequest
*)msg
.wParam
;
383 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
386 if(++deviceCount
== 1)
387 hr
= cohr
= CoInitialize(NULL
);
389 hr
= V0(proxy
,openProxy
)();
392 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
396 ReturnMsgResponse(req
, hr
);
399 case WM_USER_ResetDevice
:
400 req
= (ThreadRequest
*)msg
.wParam
;
401 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
403 hr
= V0(proxy
,resetProxy
)();
404 ReturnMsgResponse(req
, hr
);
407 case WM_USER_StartDevice
:
408 req
= (ThreadRequest
*)msg
.wParam
;
409 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
411 hr
= V0(proxy
,startProxy
)();
412 ReturnMsgResponse(req
, hr
);
415 case WM_USER_StopDevice
:
416 req
= (ThreadRequest
*)msg
.wParam
;
417 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
419 V0(proxy
,stopProxy
)();
420 ReturnMsgResponse(req
, S_OK
);
423 case WM_USER_CloseDevice
:
424 req
= (ThreadRequest
*)msg
.wParam
;
425 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
427 V0(proxy
,closeProxy
)();
428 if(--deviceCount
== 0)
431 ReturnMsgResponse(req
, S_OK
);
434 case WM_USER_Enumerate
:
435 req
= (ThreadRequest
*)msg
.wParam
;
438 if(++deviceCount
== 1)
439 hr
= cohr
= CoInitialize(NULL
);
441 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
446 if(msg
.lParam
== ALL_DEVICE_PROBE
)
447 hr
= probe_devices(Enumerator
, eRender
, &PlaybackDevices
);
448 else if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
449 hr
= probe_devices(Enumerator
, eCapture
, &CaptureDevices
);
451 IMMDeviceEnumerator_Release(Enumerator
);
455 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
458 ReturnMsgResponse(req
, hr
);
462 ERR("Unexpected message: %u\n", msg
.message
);
466 TRACE("Message loop finished\n");
472 typedef struct ALCmmdevPlayback
{
473 DERIVE_FROM_TYPE(ALCbackend
);
474 DERIVE_FROM_TYPE(ALCmmdevProxy
);
479 IAudioClient
*client
;
480 IAudioRenderClient
*render
;
485 volatile UINT32 Padding
;
487 volatile int killNow
;
491 static int ALCmmdevPlayback_mixerProc(void *arg
);
493 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
);
494 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
);
495 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*name
);
496 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
);
497 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
);
498 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
);
499 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
);
500 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
);
501 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
);
502 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
);
503 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
);
504 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
);
505 static DECLARE_FORWARD2(ALCmmdevPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
506 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, ALCuint
, availableSamples
)
507 static ClockLatency
ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback
*self
);
508 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, lock
)
509 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, unlock
)
510 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback
)
512 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback
);
513 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback
);
516 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
)
518 SET_VTABLE2(ALCmmdevPlayback
, ALCbackend
, self
);
519 SET_VTABLE2(ALCmmdevPlayback
, ALCmmdevProxy
, self
);
520 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
521 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
528 self
->NotifyEvent
= NULL
;
530 self
->MsgEvent
= NULL
;
537 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
)
539 if(self
->NotifyEvent
!= NULL
)
540 CloseHandle(self
->NotifyEvent
);
541 self
->NotifyEvent
= NULL
;
542 if(self
->MsgEvent
!= NULL
)
543 CloseHandle(self
->MsgEvent
);
544 self
->MsgEvent
= NULL
;
549 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
550 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
554 FORCE_ALIGN
static int ALCmmdevPlayback_mixerProc(void *arg
)
556 ALCmmdevPlayback
*self
= arg
;
557 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
558 UINT32 buffer_len
, written
;
559 ALuint update_size
, len
;
563 hr
= CoInitialize(NULL
);
566 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
567 V0(device
->Backend
,lock
)();
568 aluHandleDisconnect(device
);
569 V0(device
->Backend
,unlock
)();
574 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
576 update_size
= device
->UpdateSize
;
577 buffer_len
= update_size
* device
->NumUpdates
;
578 while(!self
->killNow
)
580 hr
= IAudioClient_GetCurrentPadding(self
->client
, &written
);
583 ERR("Failed to get padding: 0x%08lx\n", hr
);
584 V0(device
->Backend
,lock
)();
585 aluHandleDisconnect(device
);
586 V0(device
->Backend
,unlock
)();
589 self
->Padding
= written
;
591 len
= buffer_len
- written
;
592 if(len
< update_size
)
595 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
596 if(res
!= WAIT_OBJECT_0
)
597 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
600 len
-= len
%update_size
;
602 hr
= IAudioRenderClient_GetBuffer(self
->render
, len
, &buffer
);
605 V0(device
->Backend
,lock
)();
606 aluMixData(device
, buffer
, len
);
607 self
->Padding
= written
+ len
;
608 V0(device
->Backend
,unlock
)();
609 hr
= IAudioRenderClient_ReleaseBuffer(self
->render
, len
, 0);
613 ERR("Failed to buffer data: 0x%08lx\n", hr
);
614 V0(device
->Backend
,lock
)();
615 aluHandleDisconnect(device
);
616 V0(device
->Backend
,unlock
)();
627 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
629 memset(out
, 0, sizeof(*out
));
630 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
631 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
632 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
635 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
636 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
637 if(out
->Format
.nChannels
== 1)
638 out
->dwChannelMask
= MONO
;
639 else if(out
->Format
.nChannels
== 2)
640 out
->dwChannelMask
= STEREO
;
642 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
643 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
645 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
648 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
649 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
650 if(out
->Format
.nChannels
== 1)
651 out
->dwChannelMask
= MONO
;
652 else if(out
->Format
.nChannels
== 2)
653 out
->dwChannelMask
= STEREO
;
655 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
656 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
660 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
667 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*deviceName
)
671 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
672 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
673 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
675 ERR("Failed to create message events: %lu\n", GetLastError());
685 if(VECTOR_SIZE(PlaybackDevices
) == 0)
687 ThreadRequest req
= { self
->MsgEvent
, 0 };
688 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
689 (void)WaitForResponse(&req
);
693 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
694 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
695 if(iter
== VECTOR_END(PlaybackDevices
))
696 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
699 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
700 self
->devid
= strdupW(iter
->devid
);
701 al_string_copy(&device
->DeviceName
, iter
->name
);
710 ThreadRequest req
= { self
->MsgEvent
, 0 };
713 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
714 hr
= WaitForResponse(&req
);
716 ERR("Failed to post thread message: %lu\n", GetLastError());
721 if(self
->NotifyEvent
!= NULL
)
722 CloseHandle(self
->NotifyEvent
);
723 self
->NotifyEvent
= NULL
;
724 if(self
->MsgEvent
!= NULL
)
725 CloseHandle(self
->MsgEvent
);
726 self
->MsgEvent
= NULL
;
731 ERR("Device init failed: 0x%08lx\n", hr
);
732 return ALC_INVALID_VALUE
;
738 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
)
740 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
744 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
747 IMMDeviceEnumerator
*Enumerator
= ptr
;
749 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &self
->mmdev
);
751 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
752 IMMDeviceEnumerator_Release(Enumerator
);
756 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
760 if(al_string_empty(device
->DeviceName
))
761 get_device_name(self
->mmdev
, &device
->DeviceName
);
767 IMMDevice_Release(self
->mmdev
);
775 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
)
777 ThreadRequest req
= { self
->MsgEvent
, 0 };
779 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
780 (void)WaitForResponse(&req
);
782 CloseHandle(self
->MsgEvent
);
783 self
->MsgEvent
= NULL
;
785 CloseHandle(self
->NotifyEvent
);
786 self
->NotifyEvent
= NULL
;
792 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
)
795 IAudioClient_Release(self
->client
);
799 IMMDevice_Release(self
->mmdev
);
804 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
)
806 ThreadRequest req
= { self
->MsgEvent
, 0 };
809 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
810 hr
= WaitForResponse(&req
);
812 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
815 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
)
817 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
818 EndpointFormFactor formfactor
= UnknownFormFactor
;
819 WAVEFORMATEXTENSIBLE OutputType
;
820 WAVEFORMATEX
*wfx
= NULL
;
821 REFERENCE_TIME min_per
, buf_time
;
822 UINT32 buffer_len
, min_len
;
827 IAudioClient_Release(self
->client
);
830 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
833 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
838 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
841 ERR("Failed to get mix format: 0x%08lx\n", hr
);
845 if(!MakeExtensible(&OutputType
, wfx
))
853 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
854 device
->Frequency
-1) / device
->Frequency
;
856 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
857 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
858 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
860 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
861 device
->FmtChans
= DevFmtMono
;
862 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
863 device
->FmtChans
= DevFmtStereo
;
864 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
865 device
->FmtChans
= DevFmtQuad
;
866 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
867 device
->FmtChans
= DevFmtX51
;
868 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
869 device
->FmtChans
= DevFmtX51Rear
;
870 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
871 device
->FmtChans
= DevFmtX61
;
872 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
873 device
->FmtChans
= DevFmtX71
;
875 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
878 switch(device
->FmtChans
)
881 OutputType
.Format
.nChannels
= 1;
882 OutputType
.dwChannelMask
= MONO
;
884 case DevFmtBFormat3D
:
885 device
->FmtChans
= DevFmtStereo
;
888 OutputType
.Format
.nChannels
= 2;
889 OutputType
.dwChannelMask
= STEREO
;
892 OutputType
.Format
.nChannels
= 4;
893 OutputType
.dwChannelMask
= QUAD
;
896 OutputType
.Format
.nChannels
= 6;
897 OutputType
.dwChannelMask
= X5DOT1
;
900 OutputType
.Format
.nChannels
= 6;
901 OutputType
.dwChannelMask
= X5DOT1REAR
;
904 OutputType
.Format
.nChannels
= 7;
905 OutputType
.dwChannelMask
= X6DOT1
;
908 OutputType
.Format
.nChannels
= 8;
909 OutputType
.dwChannelMask
= X7DOT1
;
912 switch(device
->FmtType
)
915 device
->FmtType
= DevFmtUByte
;
918 OutputType
.Format
.wBitsPerSample
= 8;
919 OutputType
.Samples
.wValidBitsPerSample
= 8;
920 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
923 device
->FmtType
= DevFmtShort
;
926 OutputType
.Format
.wBitsPerSample
= 16;
927 OutputType
.Samples
.wValidBitsPerSample
= 16;
928 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
931 device
->FmtType
= DevFmtInt
;
934 OutputType
.Format
.wBitsPerSample
= 32;
935 OutputType
.Samples
.wValidBitsPerSample
= 32;
936 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
939 OutputType
.Format
.wBitsPerSample
= 32;
940 OutputType
.Samples
.wValidBitsPerSample
= 32;
941 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
944 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
946 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
947 OutputType
.Format
.wBitsPerSample
/ 8;
948 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
949 OutputType
.Format
.nBlockAlign
;
951 hr
= IAudioClient_IsFormatSupported(self
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
954 ERR("Failed to check format support: 0x%08lx\n", hr
);
955 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
959 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
965 if(!MakeExtensible(&OutputType
, wfx
))
973 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
974 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
975 device
->FmtChans
= DevFmtMono
;
976 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
977 device
->FmtChans
= DevFmtStereo
;
978 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
979 device
->FmtChans
= DevFmtQuad
;
980 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
981 device
->FmtChans
= DevFmtX51
;
982 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
983 device
->FmtChans
= DevFmtX51Rear
;
984 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
985 device
->FmtChans
= DevFmtX61
;
986 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
987 device
->FmtChans
= DevFmtX71
;
990 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
991 device
->FmtChans
= DevFmtStereo
;
992 OutputType
.Format
.nChannels
= 2;
993 OutputType
.dwChannelMask
= STEREO
;
996 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
998 if(OutputType
.Format
.wBitsPerSample
== 8)
999 device
->FmtType
= DevFmtUByte
;
1000 else if(OutputType
.Format
.wBitsPerSample
== 16)
1001 device
->FmtType
= DevFmtShort
;
1002 else if(OutputType
.Format
.wBitsPerSample
== 32)
1003 device
->FmtType
= DevFmtInt
;
1006 device
->FmtType
= DevFmtShort
;
1007 OutputType
.Format
.wBitsPerSample
= 16;
1010 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
1012 device
->FmtType
= DevFmtFloat
;
1013 OutputType
.Format
.wBitsPerSample
= 32;
1017 ERR("Unhandled format sub-type\n");
1018 device
->FmtType
= DevFmtShort
;
1019 OutputType
.Format
.wBitsPerSample
= 16;
1020 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1022 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
1024 get_device_formfactor(self
->mmdev
, &formfactor
);
1025 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&& formfactor
== Headphones
);
1027 SetDefaultWFXChannelOrder(device
);
1029 hr
= IAudioClient_Initialize(self
->client
, AUDCLNT_SHAREMODE_SHARED
,
1030 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1031 buf_time
, 0, &OutputType
.Format
, NULL
);
1034 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1038 hr
= IAudioClient_GetDevicePeriod(self
->client
, &min_per
, NULL
);
1041 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
1042 /* Find the nearest multiple of the period size to the update size */
1043 if(min_len
< device
->UpdateSize
)
1044 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
1045 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1049 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
1053 device
->UpdateSize
= min_len
;
1054 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
1055 if(device
->NumUpdates
<= 1)
1057 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1058 device
->NumUpdates
= 2;
1059 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
1062 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1065 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1073 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
)
1075 ThreadRequest req
= { self
->MsgEvent
, 0 };
1076 HRESULT hr
= E_FAIL
;
1078 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1079 hr
= WaitForResponse(&req
);
1081 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1084 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
)
1089 ResetEvent(self
->NotifyEvent
);
1090 hr
= IAudioClient_Start(self
->client
);
1092 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1095 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioRenderClient
, &ptr
);
1100 if(althrd_create(&self
->thread
, ALCmmdevPlayback_mixerProc
, self
) != althrd_success
)
1103 IAudioRenderClient_Release(self
->render
);
1104 self
->render
= NULL
;
1105 IAudioClient_Stop(self
->client
);
1106 ERR("Failed to start thread\n");
1115 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
)
1117 ThreadRequest req
= { self
->MsgEvent
, 0 };
1118 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1119 (void)WaitForResponse(&req
);
1122 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
)
1130 althrd_join(self
->thread
, &res
);
1132 IAudioRenderClient_Release(self
->render
);
1133 self
->render
= NULL
;
1134 IAudioClient_Stop(self
->client
);
1138 static ClockLatency
ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback
*self
)
1140 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1143 ALCmmdevPlayback_lock(self
);
1144 ret
.ClockTime
= GetDeviceClockTime(device
);
1145 ret
.Latency
= self
->Padding
* DEVICE_CLOCK_RES
/ device
->Frequency
;
1146 ALCmmdevPlayback_unlock(self
);
1152 typedef struct ALCmmdevCapture
{
1153 DERIVE_FROM_TYPE(ALCbackend
);
1154 DERIVE_FROM_TYPE(ALCmmdevProxy
);
1159 IAudioClient
*client
;
1160 IAudioCaptureClient
*capture
;
1165 ll_ringbuffer_t
*Ring
;
1167 volatile int killNow
;
1171 static int ALCmmdevCapture_recordProc(void *arg
);
1173 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
);
1174 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
);
1175 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*name
);
1176 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
);
1177 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
);
1178 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
);
1179 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ALCboolean
, reset
)
1180 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
);
1181 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
);
1182 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
);
1183 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
);
1184 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
);
1185 static ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
1186 static ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
);
1187 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
1188 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, lock
)
1189 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, unlock
)
1190 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture
)
1192 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture
);
1193 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture
);
1196 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
)
1198 SET_VTABLE2(ALCmmdevCapture
, ALCbackend
, self
);
1199 SET_VTABLE2(ALCmmdevCapture
, ALCmmdevProxy
, self
);
1200 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
1201 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
1206 self
->client
= NULL
;
1207 self
->capture
= NULL
;
1208 self
->NotifyEvent
= NULL
;
1210 self
->MsgEvent
= NULL
;
1217 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
)
1219 ll_ringbuffer_free(self
->Ring
);
1222 if(self
->NotifyEvent
!= NULL
)
1223 CloseHandle(self
->NotifyEvent
);
1224 self
->NotifyEvent
= NULL
;
1225 if(self
->MsgEvent
!= NULL
)
1226 CloseHandle(self
->MsgEvent
);
1227 self
->MsgEvent
= NULL
;
1232 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
1233 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
1237 FORCE_ALIGN
int ALCmmdevCapture_recordProc(void *arg
)
1239 ALCmmdevCapture
*self
= arg
;
1240 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1243 hr
= CoInitialize(NULL
);
1246 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
1247 V0(device
->Backend
,lock
)();
1248 aluHandleDisconnect(device
);
1249 V0(device
->Backend
,unlock
)();
1253 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
1255 while(!self
->killNow
)
1260 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1262 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1263 else while(avail
> 0 && SUCCEEDED(hr
))
1269 hr
= IAudioCaptureClient_GetBuffer(self
->capture
,
1270 &data
, &numsamples
, &flags
, NULL
, NULL
1274 ERR("Failed to get capture buffer: 0x%08lx\n", hr
);
1278 ll_ringbuffer_write(self
->Ring
, (char*)data
, numsamples
);
1280 hr
= IAudioCaptureClient_ReleaseBuffer(self
->capture
, numsamples
);
1283 ERR("Failed to release capture buffer: 0x%08lx\n", hr
);
1287 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1289 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1294 V0(device
->Backend
,lock
)();
1295 aluHandleDisconnect(device
);
1296 V0(device
->Backend
,unlock
)();
1300 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
1301 if(res
!= WAIT_OBJECT_0
)
1302 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
1310 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*deviceName
)
1314 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1315 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1316 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
1318 ERR("Failed to create message events: %lu\n", GetLastError());
1328 if(VECTOR_SIZE(CaptureDevices
) == 0)
1330 ThreadRequest req
= { self
->MsgEvent
, 0 };
1331 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, CAPTURE_DEVICE_PROBE
))
1332 (void)WaitForResponse(&req
);
1336 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
1337 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1338 if(iter
== VECTOR_END(CaptureDevices
))
1339 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
1342 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
1343 self
->devid
= strdupW(iter
->devid
);
1344 al_string_copy(&device
->DeviceName
, iter
->name
);
1353 ThreadRequest req
= { self
->MsgEvent
, 0 };
1356 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1357 hr
= WaitForResponse(&req
);
1359 ERR("Failed to post thread message: %lu\n", GetLastError());
1364 if(self
->NotifyEvent
!= NULL
)
1365 CloseHandle(self
->NotifyEvent
);
1366 self
->NotifyEvent
= NULL
;
1367 if(self
->MsgEvent
!= NULL
)
1368 CloseHandle(self
->MsgEvent
);
1369 self
->MsgEvent
= NULL
;
1374 ERR("Device init failed: 0x%08lx\n", hr
);
1375 return ALC_INVALID_VALUE
;
1379 ThreadRequest req
= { self
->MsgEvent
, 0 };
1382 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1383 hr
= WaitForResponse(&req
);
1385 ERR("Failed to post thread message: %lu\n", GetLastError());
1389 ALCmmdevCapture_close(self
);
1390 if(hr
== E_OUTOFMEMORY
)
1391 return ALC_OUT_OF_MEMORY
;
1392 return ALC_INVALID_VALUE
;
1396 return ALC_NO_ERROR
;
1399 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
)
1401 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1405 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
1408 IMMDeviceEnumerator
*Enumerator
= ptr
;
1410 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eCapture
, eMultimedia
, &self
->mmdev
);
1412 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
1413 IMMDeviceEnumerator_Release(Enumerator
);
1417 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1421 if(al_string_empty(device
->DeviceName
))
1422 get_device_name(self
->mmdev
, &device
->DeviceName
);
1428 IMMDevice_Release(self
->mmdev
);
1436 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
)
1438 ThreadRequest req
= { self
->MsgEvent
, 0 };
1440 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1441 (void)WaitForResponse(&req
);
1443 ll_ringbuffer_free(self
->Ring
);
1446 CloseHandle(self
->MsgEvent
);
1447 self
->MsgEvent
= NULL
;
1449 CloseHandle(self
->NotifyEvent
);
1450 self
->NotifyEvent
= NULL
;
1456 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
)
1459 IAudioClient_Release(self
->client
);
1460 self
->client
= NULL
;
1463 IMMDevice_Release(self
->mmdev
);
1468 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
)
1470 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1471 WAVEFORMATEXTENSIBLE OutputType
;
1472 WAVEFORMATEX
*wfx
= NULL
;
1473 REFERENCE_TIME buf_time
;
1479 IAudioClient_Release(self
->client
);
1480 self
->client
= NULL
;
1482 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1485 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
1490 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
1491 device
->Frequency
-1) / device
->Frequency
;
1493 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1494 switch(device
->FmtChans
)
1497 OutputType
.Format
.nChannels
= 1;
1498 OutputType
.dwChannelMask
= MONO
;
1501 OutputType
.Format
.nChannels
= 2;
1502 OutputType
.dwChannelMask
= STEREO
;
1505 OutputType
.Format
.nChannels
= 4;
1506 OutputType
.dwChannelMask
= QUAD
;
1509 OutputType
.Format
.nChannels
= 6;
1510 OutputType
.dwChannelMask
= X5DOT1
;
1513 OutputType
.Format
.nChannels
= 6;
1514 OutputType
.dwChannelMask
= X5DOT1REAR
;
1517 OutputType
.Format
.nChannels
= 7;
1518 OutputType
.dwChannelMask
= X6DOT1
;
1521 OutputType
.Format
.nChannels
= 8;
1522 OutputType
.dwChannelMask
= X7DOT1
;
1525 case DevFmtBFormat3D
:
1528 switch(device
->FmtType
)
1531 OutputType
.Format
.wBitsPerSample
= 8;
1532 OutputType
.Samples
.wValidBitsPerSample
= 8;
1533 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1536 OutputType
.Format
.wBitsPerSample
= 16;
1537 OutputType
.Samples
.wValidBitsPerSample
= 16;
1538 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1541 OutputType
.Format
.wBitsPerSample
= 32;
1542 OutputType
.Samples
.wValidBitsPerSample
= 32;
1543 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1546 OutputType
.Format
.wBitsPerSample
= 32;
1547 OutputType
.Samples
.wValidBitsPerSample
= 32;
1548 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1554 WARN("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
1557 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1559 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1560 OutputType
.Format
.wBitsPerSample
/ 8;
1561 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1562 OutputType
.Format
.nBlockAlign
;
1563 OutputType
.Format
.cbSize
= sizeof(OutputType
) - sizeof(OutputType
.Format
);
1565 hr
= IAudioClient_IsFormatSupported(self
->client
,
1566 AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
1570 ERR("Failed to check format support: 0x%08lx\n", hr
);
1574 /* FIXME: We should do conversion/resampling if we didn't get a matching format. */
1575 if(wfx
->nSamplesPerSec
!= OutputType
.Format
.nSamplesPerSec
||
1576 wfx
->wBitsPerSample
!= OutputType
.Format
.wBitsPerSample
||
1577 wfx
->nChannels
!= OutputType
.Format
.nChannels
||
1578 wfx
->nBlockAlign
!= OutputType
.Format
.nBlockAlign
)
1580 ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
1581 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1582 device
->Frequency
, wfx
->nChannels
, (wfx
->nChannels
==1)?"":"s", wfx
->wBitsPerSample
,
1583 wfx
->nSamplesPerSec
);
1588 if(!MakeExtensible(&OutputType
, wfx
))
1596 hr
= IAudioClient_Initialize(self
->client
,
1597 AUDCLNT_SHAREMODE_SHARED
, AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1598 buf_time
, 0, &OutputType
.Format
, NULL
1602 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1606 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1609 ERR("Failed to get buffer size: 0x%08lx\n", hr
);
1613 buffer_len
= maxu(device
->UpdateSize
*device
->NumUpdates
+ 1, buffer_len
);
1614 ll_ringbuffer_free(self
->Ring
);
1615 self
->Ring
= ll_ringbuffer_create(buffer_len
, OutputType
.Format
.nBlockAlign
);
1618 ERR("Failed to allocate capture ring buffer\n");
1619 return E_OUTOFMEMORY
;
1622 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1625 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1633 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
)
1635 ThreadRequest req
= { self
->MsgEvent
, 0 };
1636 HRESULT hr
= E_FAIL
;
1638 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1639 hr
= WaitForResponse(&req
);
1641 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1644 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
)
1649 ResetEvent(self
->NotifyEvent
);
1650 hr
= IAudioClient_Start(self
->client
);
1653 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1657 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioCaptureClient
, &ptr
);
1660 self
->capture
= ptr
;
1662 if(althrd_create(&self
->thread
, ALCmmdevCapture_recordProc
, self
) != althrd_success
)
1664 ERR("Failed to start thread\n");
1665 IAudioCaptureClient_Release(self
->capture
);
1666 self
->capture
= NULL
;
1673 IAudioClient_Stop(self
->client
);
1674 IAudioClient_Reset(self
->client
);
1681 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
)
1683 ThreadRequest req
= { self
->MsgEvent
, 0 };
1684 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1685 (void)WaitForResponse(&req
);
1688 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
)
1696 althrd_join(self
->thread
, &res
);
1698 IAudioCaptureClient_Release(self
->capture
);
1699 self
->capture
= NULL
;
1700 IAudioClient_Stop(self
->client
);
1701 IAudioClient_Reset(self
->client
);
1705 ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
)
1707 return (ALuint
)ll_ringbuffer_read_space(self
->Ring
);
1710 ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
1712 if(ALCmmdevCapture_availableSamples(self
) < samples
)
1713 return ALC_INVALID_VALUE
;
1714 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
1715 return ALC_NO_ERROR
;
1719 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1720 { AppendAllDevicesList(al_string_get_cstr(entry
->name
)); }
1721 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1722 { AppendCaptureDeviceList(al_string_get_cstr(entry
->name
)); }
1724 typedef struct ALCmmdevBackendFactory
{
1725 DERIVE_FROM_TYPE(ALCbackendFactory
);
1726 } ALCmmdevBackendFactory
;
1727 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1729 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
*self
);
1730 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
*self
);
1731 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
*self
, ALCbackend_Type type
);
1732 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
*self
, enum DevProbe type
);
1733 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
1735 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory
);
1738 static BOOL
MMDevApiLoad(void)
1740 static HRESULT InitResult
;
1744 InitResult
= E_FAIL
;
1746 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1747 if(req
.FinishedEvt
== NULL
)
1748 ERR("Failed to create event: %lu\n", GetLastError());
1751 ThreadHdl
= CreateThread(NULL
, 0, ALCmmdevProxy_messageHandler
, &req
, 0, &ThreadID
);
1752 if(ThreadHdl
!= NULL
)
1753 InitResult
= WaitForResponse(&req
);
1754 CloseHandle(req
.FinishedEvt
);
1757 return SUCCEEDED(InitResult
);
1760 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
* UNUSED(self
))
1762 VECTOR_INIT(PlaybackDevices
);
1763 VECTOR_INIT(CaptureDevices
);
1770 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
* UNUSED(self
))
1772 clear_devlist(&PlaybackDevices
);
1773 VECTOR_DEINIT(PlaybackDevices
);
1775 clear_devlist(&CaptureDevices
);
1776 VECTOR_DEINIT(CaptureDevices
);
1780 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
1781 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
1782 CloseHandle(ThreadHdl
);
1787 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1789 /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
1790 * rechanneling or resampling; if the device is configured for 48000hz
1791 * stereo input, for example, and the app asks for 22050hz mono,
1792 * initialization will fail.
1794 if(type
== ALCbackend_Playback
/*|| type == ALCbackend_Capture*/)
1799 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
* UNUSED(self
), enum DevProbe type
)
1801 ThreadRequest req
= { NULL
, 0 };
1803 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1804 if(req
.FinishedEvt
== NULL
)
1805 ERR("Failed to create event: %lu\n", GetLastError());
1808 HRESULT hr
= E_FAIL
;
1809 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
1810 hr
= WaitForResponse(&req
);
1811 if(SUCCEEDED(hr
)) switch(type
)
1813 case ALL_DEVICE_PROBE
:
1814 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1817 case CAPTURE_DEVICE_PROBE
:
1818 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1821 CloseHandle(req
.FinishedEvt
);
1822 req
.FinishedEvt
= NULL
;
1826 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1828 if(type
== ALCbackend_Playback
)
1830 ALCmmdevPlayback
*backend
;
1831 NEW_OBJ(backend
, ALCmmdevPlayback
)(device
);
1832 if(!backend
) return NULL
;
1833 return STATIC_CAST(ALCbackend
, backend
);
1835 if(type
== ALCbackend_Capture
)
1837 ALCmmdevCapture
*backend
;
1838 NEW_OBJ(backend
, ALCmmdevCapture
)(device
);
1839 if(!backend
) return NULL
;
1840 return STATIC_CAST(ALCbackend
, backend
);
1847 ALCbackendFactory
*ALCmmdevBackendFactory_getFactory(void)
1849 static ALCmmdevBackendFactory factory
= ALCMMDEVBACKENDFACTORY_INITIALIZER
;
1850 return STATIC_CAST(ALCbackendFactory
, &factory
);