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_
48 #include "backends/base.h"
51 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_PCM
, 0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
52 DEFINE_GUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
, 0x00000003, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71);
54 DEFINE_DEVPROPKEY(DEVPKEY_Device_FriendlyName
, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,0x20, 0x67,0xd1,0x46,0xa8,0x50,0xe0, 14);
55 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_FormFactor
, 0x1da5d803, 0xd492, 0x4edd, 0x8c,0x23, 0xe0,0xc0,0xff,0xee,0x7f,0x0e, 0);
56 DEFINE_PROPERTYKEY(PKEY_AudioEndpoint_GUID
, 0x1da5d803, 0xd492, 0x4edd, 0x8c, 0x23,0xe0, 0xc0,0xff,0xee,0x7f,0x0e, 4 );
58 #define MONO SPEAKER_FRONT_CENTER
59 #define STEREO (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT)
60 #define QUAD (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
61 #define X5DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
62 #define X5DOT1REAR (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_LEFT|SPEAKER_BACK_RIGHT)
63 #define X6DOT1 (SPEAKER_FRONT_LEFT|SPEAKER_FRONT_RIGHT|SPEAKER_FRONT_CENTER|SPEAKER_LOW_FREQUENCY|SPEAKER_BACK_CENTER|SPEAKER_SIDE_LEFT|SPEAKER_SIDE_RIGHT)
64 #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)
65 #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)
67 #define DEVNAME_HEAD "OpenAL Soft on "
72 al_string endpoint_guid
; // obtained from PKEY_AudioEndpoint_GUID , set to "Unknown device GUID" if absent.
75 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
77 static void clear_devlist(vector_DevMap
*list
)
79 #define CLEAR_DEVMAP(i) do { \
80 AL_STRING_DEINIT((i)->name); \
81 AL_STRING_DEINIT((i)->endpoint_guid); \
85 VECTOR_FOR_EACH(DevMap
, *list
, CLEAR_DEVMAP
);
86 VECTOR_RESIZE(*list
, 0, 0);
90 static vector_DevMap PlaybackDevices
;
91 static vector_DevMap CaptureDevices
;
94 static HANDLE ThreadHdl
;
95 static DWORD ThreadID
;
102 #define WM_USER_First (WM_USER+0)
103 #define WM_USER_OpenDevice (WM_USER+0)
104 #define WM_USER_ResetDevice (WM_USER+1)
105 #define WM_USER_StartDevice (WM_USER+2)
106 #define WM_USER_StopDevice (WM_USER+3)
107 #define WM_USER_CloseDevice (WM_USER+4)
108 #define WM_USER_Enumerate (WM_USER+5)
109 #define WM_USER_Last (WM_USER+5)
111 static inline void ReturnMsgResponse(ThreadRequest
*req
, HRESULT res
)
114 SetEvent(req
->FinishedEvt
);
117 static HRESULT
WaitForResponse(ThreadRequest
*req
)
119 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
121 ERR("Message response error: %lu\n", GetLastError());
126 static void get_device_name_and_guid(IMMDevice
*device
, al_string
*name
, al_string
*guid
)
133 al_string_copy_cstr(name
, DEVNAME_HEAD
);
135 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
138 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
139 al_string_append_cstr(name
, "Unknown Device Name");
140 if(guid
!=NULL
)al_string_copy_cstr(guid
, "Unknown Device GUID");
144 PropVariantInit(&pvname
);
146 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
149 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr
);
150 al_string_append_cstr(name
, "Unknown Device Name");
152 else if(pvname
.vt
== VT_LPWSTR
)
153 al_string_append_wcstr(name
, pvname
.pwszVal
);
156 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvname
.vt
);
157 al_string_append_cstr(name
, "Unknown Device Name");
159 PropVariantClear(&pvname
);
162 PropVariantInit(&pvguid
);
164 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&PKEY_AudioEndpoint_GUID
, &pvguid
);
167 WARN("GetValue AudioEndpoint_GUID failed: 0x%08lx\n", hr
);
168 al_string_copy_cstr(guid
, "Unknown Device GUID");
170 else if(pvguid
.vt
== VT_LPWSTR
)
171 al_string_copy_wcstr(guid
, pvguid
.pwszVal
);
174 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvguid
.vt
);
175 al_string_copy_cstr(guid
, "Unknown Device GUID");
178 PropVariantClear(&pvguid
);
181 IPropertyStore_Release(ps
);
184 static void get_device_formfactor(IMMDevice
*device
, EndpointFormFactor
*formfactor
)
190 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
193 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
197 PropVariantInit(&pvform
);
199 hr
= IPropertyStore_GetValue(ps
, &PKEY_AudioEndpoint_FormFactor
, &pvform
);
201 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr
);
202 else if(pvform
.vt
== VT_UI4
)
203 *formfactor
= pvform
.ulVal
;
204 else if(pvform
.vt
== VT_EMPTY
)
205 *formfactor
= UnknownFormFactor
;
207 WARN("Unexpected PROPVARIANT type: 0x%04x\n", pvform
.vt
);
209 PropVariantClear(&pvform
);
210 IPropertyStore_Release(ps
);
214 static void add_device(IMMDevice
*device
, const WCHAR
*devid
, vector_DevMap
*list
)
220 AL_STRING_INIT(tmpname
);
221 AL_STRING_INIT(entry
.name
);
222 AL_STRING_INIT(entry
.endpoint_guid
);
224 entry
.devid
= strdupW(devid
);
225 get_device_name_and_guid(device
, &tmpname
, &entry
.endpoint_guid
);
231 al_string_copy(&entry
.name
, tmpname
);
235 snprintf(str
, sizeof(str
), " #%d", count
+1);
236 al_string_append_cstr(&entry
.name
, str
);
239 #define MATCH_ENTRY(i) (al_string_cmp(entry.name, (i)->name) == 0)
240 VECTOR_FIND_IF(iter
, const DevMap
, *list
, MATCH_ENTRY
);
241 if(iter
== VECTOR_END(*list
)) break;
246 TRACE("Got device \"%s\", \"%s\", \"%ls\"\n", al_string_get_cstr(entry
.name
), al_string_get_cstr(entry
.endpoint_guid
), entry
.devid
);
247 VECTOR_PUSH_BACK(*list
, entry
);
249 AL_STRING_DEINIT(tmpname
);
252 static WCHAR
*get_device_id(IMMDevice
*device
)
257 hr
= IMMDevice_GetId(device
, &devid
);
260 ERR("Failed to get device id: %lx\n", hr
);
267 static HRESULT
probe_devices(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, vector_DevMap
*list
)
269 IMMDeviceCollection
*coll
;
270 IMMDevice
*defdev
= NULL
;
271 WCHAR
*defdevid
= NULL
;
276 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
279 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
284 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
285 if(SUCCEEDED(hr
) && count
> 0)
288 VECTOR_RESIZE(*list
, 0, count
);
290 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, flowdir
,
291 eMultimedia
, &defdev
);
293 if(SUCCEEDED(hr
) && defdev
!= NULL
)
295 defdevid
= get_device_id(defdev
);
297 add_device(defdev
, defdevid
, list
);
300 for(i
= 0;i
< count
;++i
)
305 hr
= IMMDeviceCollection_Item(coll
, i
, &device
);
306 if(FAILED(hr
)) continue;
308 devid
= get_device_id(device
);
311 if(wcscmp(devid
, defdevid
) != 0)
312 add_device(device
, devid
, list
);
313 CoTaskMemFree(devid
);
315 IMMDevice_Release(device
);
318 if(defdev
) IMMDevice_Release(defdev
);
319 if(defdevid
) CoTaskMemFree(defdevid
);
320 IMMDeviceCollection_Release(coll
);
326 /* Proxy interface used by the message handler. */
327 struct ALCmmdevProxyVtable
;
329 typedef struct ALCmmdevProxy
{
330 const struct ALCmmdevProxyVtable
*vtbl
;
333 struct ALCmmdevProxyVtable
{
334 HRESULT (*const openProxy
)(ALCmmdevProxy
*);
335 void (*const closeProxy
)(ALCmmdevProxy
*);
337 HRESULT (*const resetProxy
)(ALCmmdevProxy
*);
338 HRESULT (*const startProxy
)(ALCmmdevProxy
*);
339 void (*const stopProxy
)(ALCmmdevProxy
*);
342 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
343 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
344 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
345 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
346 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
347 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
349 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
350 T##_ALCmmdevProxy_openProxy, \
351 T##_ALCmmdevProxy_closeProxy, \
352 T##_ALCmmdevProxy_resetProxy, \
353 T##_ALCmmdevProxy_startProxy, \
354 T##_ALCmmdevProxy_stopProxy, \
357 static void ALCmmdevProxy_Construct(ALCmmdevProxy
* UNUSED(self
)) { }
358 static void ALCmmdevProxy_Destruct(ALCmmdevProxy
* UNUSED(self
)) { }
360 static DWORD CALLBACK
ALCmmdevProxy_messageHandler(void *ptr
)
362 ThreadRequest
*req
= ptr
;
363 IMMDeviceEnumerator
*Enumerator
;
364 ALuint deviceCount
= 0;
365 ALCmmdevProxy
*proxy
;
369 TRACE("Starting message thread\n");
371 cohr
= CoInitialize(NULL
);
374 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
375 ReturnMsgResponse(req
, cohr
);
379 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
382 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
384 ReturnMsgResponse(req
, hr
);
388 IMMDeviceEnumerator_Release(Enumerator
);
393 /* HACK: Force Windows to create a message queue for this thread before
394 * returning success, otherwise PostThreadMessage may fail if it gets
395 * called before GetMessage.
397 PeekMessage(&msg
, NULL
, WM_USER
, WM_USER
, PM_NOREMOVE
);
399 TRACE("Message thread initialization complete\n");
400 ReturnMsgResponse(req
, S_OK
);
402 TRACE("Starting message loop\n");
403 while(GetMessage(&msg
, NULL
, WM_USER_First
, WM_USER_Last
))
405 TRACE("Got message %u (lparam=%p, wparam=%p)\n", msg
.message
, (void*)msg
.lParam
, (void*)msg
.wParam
);
408 case WM_USER_OpenDevice
:
409 req
= (ThreadRequest
*)msg
.wParam
;
410 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
413 if(++deviceCount
== 1)
414 hr
= cohr
= CoInitialize(NULL
);
416 hr
= V0(proxy
,openProxy
)();
419 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
423 ReturnMsgResponse(req
, hr
);
426 case WM_USER_ResetDevice
:
427 req
= (ThreadRequest
*)msg
.wParam
;
428 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
430 hr
= V0(proxy
,resetProxy
)();
431 ReturnMsgResponse(req
, hr
);
434 case WM_USER_StartDevice
:
435 req
= (ThreadRequest
*)msg
.wParam
;
436 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
438 hr
= V0(proxy
,startProxy
)();
439 ReturnMsgResponse(req
, hr
);
442 case WM_USER_StopDevice
:
443 req
= (ThreadRequest
*)msg
.wParam
;
444 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
446 V0(proxy
,stopProxy
)();
447 ReturnMsgResponse(req
, S_OK
);
450 case WM_USER_CloseDevice
:
451 req
= (ThreadRequest
*)msg
.wParam
;
452 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
454 V0(proxy
,closeProxy
)();
455 if(--deviceCount
== 0)
458 ReturnMsgResponse(req
, S_OK
);
461 case WM_USER_Enumerate
:
462 req
= (ThreadRequest
*)msg
.wParam
;
465 if(++deviceCount
== 1)
466 hr
= cohr
= CoInitialize(NULL
);
468 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
473 if(msg
.lParam
== ALL_DEVICE_PROBE
)
474 hr
= probe_devices(Enumerator
, eRender
, &PlaybackDevices
);
475 else if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
476 hr
= probe_devices(Enumerator
, eCapture
, &CaptureDevices
);
478 IMMDeviceEnumerator_Release(Enumerator
);
482 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
485 ReturnMsgResponse(req
, hr
);
489 ERR("Unexpected message: %u\n", msg
.message
);
493 TRACE("Message loop finished\n");
499 typedef struct ALCmmdevPlayback
{
500 DERIVE_FROM_TYPE(ALCbackend
);
501 DERIVE_FROM_TYPE(ALCmmdevProxy
);
506 IAudioClient
*client
;
507 IAudioRenderClient
*render
;
512 volatile UINT32 Padding
;
514 volatile int killNow
;
518 static int ALCmmdevPlayback_mixerProc(void *arg
);
520 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
);
521 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
);
522 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*name
);
523 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
);
524 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
);
525 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
);
526 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
);
527 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
);
528 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
);
529 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
);
530 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
);
531 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
);
532 static DECLARE_FORWARD2(ALCmmdevPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
533 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, ALCuint
, availableSamples
)
534 static ClockLatency
ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback
*self
);
535 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, lock
)
536 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, unlock
)
537 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback
)
539 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback
);
540 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback
);
543 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
)
545 SET_VTABLE2(ALCmmdevPlayback
, ALCbackend
, self
);
546 SET_VTABLE2(ALCmmdevPlayback
, ALCmmdevProxy
, self
);
547 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
548 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
555 self
->NotifyEvent
= NULL
;
557 self
->MsgEvent
= NULL
;
564 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
)
566 if(self
->NotifyEvent
!= NULL
)
567 CloseHandle(self
->NotifyEvent
);
568 self
->NotifyEvent
= NULL
;
569 if(self
->MsgEvent
!= NULL
)
570 CloseHandle(self
->MsgEvent
);
571 self
->MsgEvent
= NULL
;
576 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
577 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
581 FORCE_ALIGN
static int ALCmmdevPlayback_mixerProc(void *arg
)
583 ALCmmdevPlayback
*self
= arg
;
584 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
585 UINT32 buffer_len
, written
;
586 ALuint update_size
, len
;
590 hr
= CoInitialize(NULL
);
593 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
594 V0(device
->Backend
,lock
)();
595 aluHandleDisconnect(device
);
596 V0(device
->Backend
,unlock
)();
601 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
603 update_size
= device
->UpdateSize
;
604 buffer_len
= update_size
* device
->NumUpdates
;
605 while(!self
->killNow
)
607 hr
= IAudioClient_GetCurrentPadding(self
->client
, &written
);
610 ERR("Failed to get padding: 0x%08lx\n", hr
);
611 V0(device
->Backend
,lock
)();
612 aluHandleDisconnect(device
);
613 V0(device
->Backend
,unlock
)();
616 self
->Padding
= written
;
618 len
= buffer_len
- written
;
619 if(len
< update_size
)
622 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
623 if(res
!= WAIT_OBJECT_0
)
624 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
627 len
-= len
%update_size
;
629 hr
= IAudioRenderClient_GetBuffer(self
->render
, len
, &buffer
);
632 ALCmmdevPlayback_lock(self
);
633 aluMixData(device
, buffer
, len
);
634 self
->Padding
= written
+ len
;
635 ALCmmdevPlayback_unlock(self
);
636 hr
= IAudioRenderClient_ReleaseBuffer(self
->render
, len
, 0);
640 ERR("Failed to buffer data: 0x%08lx\n", hr
);
641 V0(device
->Backend
,lock
)();
642 aluHandleDisconnect(device
);
643 V0(device
->Backend
,unlock
)();
654 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
656 memset(out
, 0, sizeof(*out
));
657 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
658 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
659 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
662 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
663 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
664 if(out
->Format
.nChannels
== 1)
665 out
->dwChannelMask
= MONO
;
666 else if(out
->Format
.nChannels
== 2)
667 out
->dwChannelMask
= STEREO
;
669 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
670 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
672 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
675 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
676 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
677 if(out
->Format
.nChannels
== 1)
678 out
->dwChannelMask
= MONO
;
679 else if(out
->Format
.nChannels
== 2)
680 out
->dwChannelMask
= STEREO
;
682 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
683 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
687 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
693 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*deviceName
)
697 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
698 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
699 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
701 ERR("Failed to create message events: %lu\n", GetLastError());
711 if(VECTOR_SIZE(PlaybackDevices
) == 0)
713 ThreadRequest req
= { self
->MsgEvent
, 0 };
714 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
715 (void)WaitForResponse(&req
);
719 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 || \
720 al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
721 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
723 if(iter
== VECTOR_END(PlaybackDevices
))
726 if((len
=MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, NULL
, 0)) > 0)
728 WCHAR
*wname
= calloc(sizeof(WCHAR
), len
);
729 MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, wname
, len
);
730 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
731 VECTOR_FIND_IF(iter
, const DevMap
, PlaybackDevices
, MATCH_NAME
);
736 if(iter
== VECTOR_END(PlaybackDevices
))
737 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
740 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
741 self
->devid
= strdupW(iter
->devid
);
742 al_string_copy(&device
->DeviceName
, iter
->name
);
750 ThreadRequest req
= { self
->MsgEvent
, 0 };
753 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
754 hr
= WaitForResponse(&req
);
756 ERR("Failed to post thread message: %lu\n", GetLastError());
761 if(self
->NotifyEvent
!= NULL
)
762 CloseHandle(self
->NotifyEvent
);
763 self
->NotifyEvent
= NULL
;
764 if(self
->MsgEvent
!= NULL
)
765 CloseHandle(self
->MsgEvent
);
766 self
->MsgEvent
= NULL
;
771 ERR("Device init failed: 0x%08lx\n", hr
);
772 return ALC_INVALID_VALUE
;
778 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
)
780 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
784 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
787 IMMDeviceEnumerator
*Enumerator
= ptr
;
789 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &self
->mmdev
);
791 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
792 IMMDeviceEnumerator_Release(Enumerator
);
796 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
800 if(al_string_empty(device
->DeviceName
))
801 get_device_name_and_guid(self
->mmdev
, &device
->DeviceName
, NULL
);
807 IMMDevice_Release(self
->mmdev
);
815 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
)
817 ThreadRequest req
= { self
->MsgEvent
, 0 };
819 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
820 (void)WaitForResponse(&req
);
822 CloseHandle(self
->MsgEvent
);
823 self
->MsgEvent
= NULL
;
825 CloseHandle(self
->NotifyEvent
);
826 self
->NotifyEvent
= NULL
;
832 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
)
835 IAudioClient_Release(self
->client
);
839 IMMDevice_Release(self
->mmdev
);
844 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
)
846 ThreadRequest req
= { self
->MsgEvent
, 0 };
849 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
850 hr
= WaitForResponse(&req
);
852 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
855 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
)
857 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
858 EndpointFormFactor formfactor
= UnknownFormFactor
;
859 WAVEFORMATEXTENSIBLE OutputType
;
860 WAVEFORMATEX
*wfx
= NULL
;
861 REFERENCE_TIME min_per
, buf_time
;
862 UINT32 buffer_len
, min_len
;
867 IAudioClient_Release(self
->client
);
870 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
873 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
878 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
881 ERR("Failed to get mix format: 0x%08lx\n", hr
);
885 if(!MakeExtensible(&OutputType
, wfx
))
893 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
894 device
->Frequency
-1) / device
->Frequency
;
896 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
897 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
898 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
900 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
901 device
->FmtChans
= DevFmtMono
;
902 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
903 device
->FmtChans
= DevFmtStereo
;
904 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
905 device
->FmtChans
= DevFmtQuad
;
906 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
907 device
->FmtChans
= DevFmtX51
;
908 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
909 device
->FmtChans
= DevFmtX51Rear
;
910 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
911 device
->FmtChans
= DevFmtX61
;
912 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
913 device
->FmtChans
= DevFmtX71
;
915 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
918 switch(device
->FmtChans
)
921 OutputType
.Format
.nChannels
= 1;
922 OutputType
.dwChannelMask
= MONO
;
927 device
->FmtChans
= DevFmtStereo
;
930 OutputType
.Format
.nChannels
= 2;
931 OutputType
.dwChannelMask
= STEREO
;
934 OutputType
.Format
.nChannels
= 4;
935 OutputType
.dwChannelMask
= QUAD
;
938 OutputType
.Format
.nChannels
= 6;
939 OutputType
.dwChannelMask
= X5DOT1
;
942 OutputType
.Format
.nChannels
= 6;
943 OutputType
.dwChannelMask
= X5DOT1REAR
;
946 OutputType
.Format
.nChannels
= 7;
947 OutputType
.dwChannelMask
= X6DOT1
;
950 OutputType
.Format
.nChannels
= 8;
951 OutputType
.dwChannelMask
= X7DOT1
;
954 switch(device
->FmtType
)
957 device
->FmtType
= DevFmtUByte
;
960 OutputType
.Format
.wBitsPerSample
= 8;
961 OutputType
.Samples
.wValidBitsPerSample
= 8;
962 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
965 device
->FmtType
= DevFmtShort
;
968 OutputType
.Format
.wBitsPerSample
= 16;
969 OutputType
.Samples
.wValidBitsPerSample
= 16;
970 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
973 device
->FmtType
= DevFmtInt
;
976 OutputType
.Format
.wBitsPerSample
= 32;
977 OutputType
.Samples
.wValidBitsPerSample
= 32;
978 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
981 OutputType
.Format
.wBitsPerSample
= 32;
982 OutputType
.Samples
.wValidBitsPerSample
= 32;
983 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
986 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
988 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
989 OutputType
.Format
.wBitsPerSample
/ 8;
990 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
991 OutputType
.Format
.nBlockAlign
;
993 hr
= IAudioClient_IsFormatSupported(self
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
996 ERR("Failed to check format support: 0x%08lx\n", hr
);
997 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
1001 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
1007 if(!MakeExtensible(&OutputType
, wfx
))
1015 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
1016 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
1017 device
->FmtChans
= DevFmtMono
;
1018 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
1019 device
->FmtChans
= DevFmtStereo
;
1020 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
1021 device
->FmtChans
= DevFmtQuad
;
1022 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
1023 device
->FmtChans
= DevFmtX51
;
1024 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
1025 device
->FmtChans
= DevFmtX51Rear
;
1026 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
1027 device
->FmtChans
= DevFmtX61
;
1028 else if(OutputType
.Format
.nChannels
== 8 && (OutputType
.dwChannelMask
== X7DOT1
|| OutputType
.dwChannelMask
== X7DOT1_WIDE
))
1029 device
->FmtChans
= DevFmtX71
;
1032 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
1033 device
->FmtChans
= DevFmtStereo
;
1034 OutputType
.Format
.nChannels
= 2;
1035 OutputType
.dwChannelMask
= STEREO
;
1038 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
1040 if(OutputType
.Format
.wBitsPerSample
== 8)
1041 device
->FmtType
= DevFmtUByte
;
1042 else if(OutputType
.Format
.wBitsPerSample
== 16)
1043 device
->FmtType
= DevFmtShort
;
1044 else if(OutputType
.Format
.wBitsPerSample
== 32)
1045 device
->FmtType
= DevFmtInt
;
1048 device
->FmtType
= DevFmtShort
;
1049 OutputType
.Format
.wBitsPerSample
= 16;
1052 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
1054 device
->FmtType
= DevFmtFloat
;
1055 OutputType
.Format
.wBitsPerSample
= 32;
1059 ERR("Unhandled format sub-type\n");
1060 device
->FmtType
= DevFmtShort
;
1061 OutputType
.Format
.wBitsPerSample
= 16;
1062 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1064 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
1066 get_device_formfactor(self
->mmdev
, &formfactor
);
1067 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&&
1068 (formfactor
== Headphones
|| formfactor
== Headset
)
1071 SetDefaultWFXChannelOrder(device
);
1073 hr
= IAudioClient_Initialize(self
->client
, AUDCLNT_SHAREMODE_SHARED
,
1074 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1075 buf_time
, 0, &OutputType
.Format
, NULL
);
1078 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1082 hr
= IAudioClient_GetDevicePeriod(self
->client
, &min_per
, NULL
);
1085 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
1086 /* Find the nearest multiple of the period size to the update size */
1087 if(min_len
< device
->UpdateSize
)
1088 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
1089 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1093 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
1097 device
->UpdateSize
= min_len
;
1098 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
1099 if(device
->NumUpdates
<= 1)
1101 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1102 device
->NumUpdates
= 2;
1103 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
1106 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1109 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1117 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
)
1119 ThreadRequest req
= { self
->MsgEvent
, 0 };
1120 HRESULT hr
= E_FAIL
;
1122 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1123 hr
= WaitForResponse(&req
);
1125 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1128 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
)
1133 ResetEvent(self
->NotifyEvent
);
1134 hr
= IAudioClient_Start(self
->client
);
1136 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1139 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioRenderClient
, &ptr
);
1144 if(althrd_create(&self
->thread
, ALCmmdevPlayback_mixerProc
, self
) != althrd_success
)
1147 IAudioRenderClient_Release(self
->render
);
1148 self
->render
= NULL
;
1149 IAudioClient_Stop(self
->client
);
1150 ERR("Failed to start thread\n");
1159 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
)
1161 ThreadRequest req
= { self
->MsgEvent
, 0 };
1162 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1163 (void)WaitForResponse(&req
);
1166 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
)
1174 althrd_join(self
->thread
, &res
);
1176 IAudioRenderClient_Release(self
->render
);
1177 self
->render
= NULL
;
1178 IAudioClient_Stop(self
->client
);
1182 static ClockLatency
ALCmmdevPlayback_getClockLatency(ALCmmdevPlayback
*self
)
1184 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1187 ALCmmdevPlayback_lock(self
);
1188 ret
.ClockTime
= GetDeviceClockTime(device
);
1189 ret
.Latency
= self
->Padding
* DEVICE_CLOCK_RES
/ device
->Frequency
;
1190 ALCmmdevPlayback_unlock(self
);
1196 typedef struct ALCmmdevCapture
{
1197 DERIVE_FROM_TYPE(ALCbackend
);
1198 DERIVE_FROM_TYPE(ALCmmdevProxy
);
1203 IAudioClient
*client
;
1204 IAudioCaptureClient
*capture
;
1209 ll_ringbuffer_t
*Ring
;
1211 volatile int killNow
;
1215 static int ALCmmdevCapture_recordProc(void *arg
);
1217 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
);
1218 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
);
1219 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*name
);
1220 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
);
1221 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
);
1222 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
);
1223 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ALCboolean
, reset
)
1224 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
);
1225 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
);
1226 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
);
1227 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
);
1228 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
);
1229 static ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
1230 static ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
);
1231 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ClockLatency
, getClockLatency
)
1232 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, lock
)
1233 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, unlock
)
1234 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture
)
1236 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture
);
1237 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture
);
1240 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
)
1242 SET_VTABLE2(ALCmmdevCapture
, ALCbackend
, self
);
1243 SET_VTABLE2(ALCmmdevCapture
, ALCmmdevProxy
, self
);
1244 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
1245 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
1250 self
->client
= NULL
;
1251 self
->capture
= NULL
;
1252 self
->NotifyEvent
= NULL
;
1254 self
->MsgEvent
= NULL
;
1261 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
)
1263 ll_ringbuffer_free(self
->Ring
);
1266 if(self
->NotifyEvent
!= NULL
)
1267 CloseHandle(self
->NotifyEvent
);
1268 self
->NotifyEvent
= NULL
;
1269 if(self
->MsgEvent
!= NULL
)
1270 CloseHandle(self
->MsgEvent
);
1271 self
->MsgEvent
= NULL
;
1276 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
1277 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
1281 FORCE_ALIGN
int ALCmmdevCapture_recordProc(void *arg
)
1283 ALCmmdevCapture
*self
= arg
;
1284 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1287 hr
= CoInitialize(NULL
);
1290 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
1291 V0(device
->Backend
,lock
)();
1292 aluHandleDisconnect(device
);
1293 V0(device
->Backend
,unlock
)();
1297 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
1299 while(!self
->killNow
)
1304 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1306 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1307 else while(avail
> 0 && SUCCEEDED(hr
))
1313 hr
= IAudioCaptureClient_GetBuffer(self
->capture
,
1314 &data
, &numsamples
, &flags
, NULL
, NULL
1318 ERR("Failed to get capture buffer: 0x%08lx\n", hr
);
1322 ll_ringbuffer_write(self
->Ring
, (char*)data
, numsamples
);
1324 hr
= IAudioCaptureClient_ReleaseBuffer(self
->capture
, numsamples
);
1327 ERR("Failed to release capture buffer: 0x%08lx\n", hr
);
1331 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1333 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1338 V0(device
->Backend
,lock
)();
1339 aluHandleDisconnect(device
);
1340 V0(device
->Backend
,unlock
)();
1344 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
1345 if(res
!= WAIT_OBJECT_0
)
1346 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
1354 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*deviceName
)
1358 self
->NotifyEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1359 self
->MsgEvent
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1360 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
1362 ERR("Failed to create message events: %lu\n", GetLastError());
1372 if(VECTOR_SIZE(CaptureDevices
) == 0)
1374 ThreadRequest req
= { self
->MsgEvent
, 0 };
1375 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, CAPTURE_DEVICE_PROBE
))
1376 (void)WaitForResponse(&req
);
1380 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0 || \
1381 al_string_cmp_cstr((i)->endpoint_guid, deviceName) == 0)
1382 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1384 if(iter
== VECTOR_END(CaptureDevices
))
1387 if((len
=MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, NULL
, 0)) > 0)
1389 WCHAR
*wname
= calloc(sizeof(WCHAR
), len
);
1390 MultiByteToWideChar(CP_UTF8
, 0, deviceName
, -1, wname
, len
);
1391 #define MATCH_NAME(i) (wcscmp((i)->devid, wname) == 0)
1392 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1397 if(iter
== VECTOR_END(CaptureDevices
))
1398 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
1401 ALCdevice
*device
= STATIC_CAST(ALCbackend
,self
)->mDevice
;
1402 self
->devid
= strdupW(iter
->devid
);
1403 al_string_copy(&device
->DeviceName
, iter
->name
);
1411 ThreadRequest req
= { self
->MsgEvent
, 0 };
1414 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1415 hr
= WaitForResponse(&req
);
1417 ERR("Failed to post thread message: %lu\n", GetLastError());
1422 if(self
->NotifyEvent
!= NULL
)
1423 CloseHandle(self
->NotifyEvent
);
1424 self
->NotifyEvent
= NULL
;
1425 if(self
->MsgEvent
!= NULL
)
1426 CloseHandle(self
->MsgEvent
);
1427 self
->MsgEvent
= NULL
;
1432 ERR("Device init failed: 0x%08lx\n", hr
);
1433 return ALC_INVALID_VALUE
;
1437 ThreadRequest req
= { self
->MsgEvent
, 0 };
1440 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1441 hr
= WaitForResponse(&req
);
1443 ERR("Failed to post thread message: %lu\n", GetLastError());
1447 ALCmmdevCapture_close(self
);
1448 if(hr
== E_OUTOFMEMORY
)
1449 return ALC_OUT_OF_MEMORY
;
1450 return ALC_INVALID_VALUE
;
1454 return ALC_NO_ERROR
;
1457 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
)
1459 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1463 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
1466 IMMDeviceEnumerator
*Enumerator
= ptr
;
1468 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eCapture
, eMultimedia
, &self
->mmdev
);
1470 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
1471 IMMDeviceEnumerator_Release(Enumerator
);
1475 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1479 if(al_string_empty(device
->DeviceName
))
1480 get_device_name_and_guid(self
->mmdev
, &device
->DeviceName
, NULL
);
1486 IMMDevice_Release(self
->mmdev
);
1494 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
)
1496 ThreadRequest req
= { self
->MsgEvent
, 0 };
1498 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1499 (void)WaitForResponse(&req
);
1501 ll_ringbuffer_free(self
->Ring
);
1504 CloseHandle(self
->MsgEvent
);
1505 self
->MsgEvent
= NULL
;
1507 CloseHandle(self
->NotifyEvent
);
1508 self
->NotifyEvent
= NULL
;
1514 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
)
1517 IAudioClient_Release(self
->client
);
1518 self
->client
= NULL
;
1521 IMMDevice_Release(self
->mmdev
);
1526 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
)
1528 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1529 WAVEFORMATEXTENSIBLE OutputType
;
1530 WAVEFORMATEX
*wfx
= NULL
;
1531 REFERENCE_TIME buf_time
;
1537 IAudioClient_Release(self
->client
);
1538 self
->client
= NULL
;
1540 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1543 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
1548 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
1549 device
->Frequency
-1) / device
->Frequency
;
1551 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1552 switch(device
->FmtChans
)
1555 OutputType
.Format
.nChannels
= 1;
1556 OutputType
.dwChannelMask
= MONO
;
1559 OutputType
.Format
.nChannels
= 2;
1560 OutputType
.dwChannelMask
= STEREO
;
1563 OutputType
.Format
.nChannels
= 4;
1564 OutputType
.dwChannelMask
= QUAD
;
1567 OutputType
.Format
.nChannels
= 6;
1568 OutputType
.dwChannelMask
= X5DOT1
;
1571 OutputType
.Format
.nChannels
= 6;
1572 OutputType
.dwChannelMask
= X5DOT1REAR
;
1575 OutputType
.Format
.nChannels
= 7;
1576 OutputType
.dwChannelMask
= X6DOT1
;
1579 OutputType
.Format
.nChannels
= 8;
1580 OutputType
.dwChannelMask
= X7DOT1
;
1588 switch(device
->FmtType
)
1591 OutputType
.Format
.wBitsPerSample
= 8;
1592 OutputType
.Samples
.wValidBitsPerSample
= 8;
1593 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1596 OutputType
.Format
.wBitsPerSample
= 16;
1597 OutputType
.Samples
.wValidBitsPerSample
= 16;
1598 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1601 OutputType
.Format
.wBitsPerSample
= 32;
1602 OutputType
.Samples
.wValidBitsPerSample
= 32;
1603 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1606 OutputType
.Format
.wBitsPerSample
= 32;
1607 OutputType
.Samples
.wValidBitsPerSample
= 32;
1608 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1614 WARN("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
1617 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1619 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1620 OutputType
.Format
.wBitsPerSample
/ 8;
1621 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1622 OutputType
.Format
.nBlockAlign
;
1623 OutputType
.Format
.cbSize
= sizeof(OutputType
) - sizeof(OutputType
.Format
);
1625 hr
= IAudioClient_IsFormatSupported(self
->client
,
1626 AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
1630 ERR("Failed to check format support: 0x%08lx\n", hr
);
1634 /* FIXME: We should do conversion/resampling if we didn't get a matching format. */
1635 if(wfx
->nSamplesPerSec
!= OutputType
.Format
.nSamplesPerSec
||
1636 wfx
->wBitsPerSample
!= OutputType
.Format
.wBitsPerSample
||
1637 wfx
->nChannels
!= OutputType
.Format
.nChannels
||
1638 wfx
->nBlockAlign
!= OutputType
.Format
.nBlockAlign
)
1640 ERR("Failed to get matching format, wanted: %s %s %uhz, got: %d channel%s %d-bit %luhz\n",
1641 DevFmtChannelsString(device
->FmtChans
), DevFmtTypeString(device
->FmtType
),
1642 device
->Frequency
, wfx
->nChannels
, (wfx
->nChannels
==1)?"":"s", wfx
->wBitsPerSample
,
1643 wfx
->nSamplesPerSec
);
1648 if(!MakeExtensible(&OutputType
, wfx
))
1656 hr
= IAudioClient_Initialize(self
->client
,
1657 AUDCLNT_SHAREMODE_SHARED
, AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1658 buf_time
, 0, &OutputType
.Format
, NULL
1662 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1666 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1669 ERR("Failed to get buffer size: 0x%08lx\n", hr
);
1673 buffer_len
= maxu(device
->UpdateSize
*device
->NumUpdates
+ 1, buffer_len
);
1674 ll_ringbuffer_free(self
->Ring
);
1675 self
->Ring
= ll_ringbuffer_create(buffer_len
, OutputType
.Format
.nBlockAlign
);
1678 ERR("Failed to allocate capture ring buffer\n");
1679 return E_OUTOFMEMORY
;
1682 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1685 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1693 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
)
1695 ThreadRequest req
= { self
->MsgEvent
, 0 };
1696 HRESULT hr
= E_FAIL
;
1698 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1699 hr
= WaitForResponse(&req
);
1701 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1704 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
)
1709 ResetEvent(self
->NotifyEvent
);
1710 hr
= IAudioClient_Start(self
->client
);
1713 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1717 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioCaptureClient
, &ptr
);
1720 self
->capture
= ptr
;
1722 if(althrd_create(&self
->thread
, ALCmmdevCapture_recordProc
, self
) != althrd_success
)
1724 ERR("Failed to start thread\n");
1725 IAudioCaptureClient_Release(self
->capture
);
1726 self
->capture
= NULL
;
1733 IAudioClient_Stop(self
->client
);
1734 IAudioClient_Reset(self
->client
);
1741 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
)
1743 ThreadRequest req
= { self
->MsgEvent
, 0 };
1744 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1745 (void)WaitForResponse(&req
);
1748 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
)
1756 althrd_join(self
->thread
, &res
);
1758 IAudioCaptureClient_Release(self
->capture
);
1759 self
->capture
= NULL
;
1760 IAudioClient_Stop(self
->client
);
1761 IAudioClient_Reset(self
->client
);
1765 ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
)
1767 return (ALuint
)ll_ringbuffer_read_space(self
->Ring
);
1770 ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
1772 if(ALCmmdevCapture_availableSamples(self
) < samples
)
1773 return ALC_INVALID_VALUE
;
1774 ll_ringbuffer_read(self
->Ring
, buffer
, samples
);
1775 return ALC_NO_ERROR
;
1779 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1780 { AppendAllDevicesList(al_string_get_cstr(entry
->name
)); }
1781 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1782 { AppendCaptureDeviceList(al_string_get_cstr(entry
->name
)); }
1784 typedef struct ALCmmdevBackendFactory
{
1785 DERIVE_FROM_TYPE(ALCbackendFactory
);
1786 } ALCmmdevBackendFactory
;
1787 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1789 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
*self
);
1790 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
*self
);
1791 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
*self
, ALCbackend_Type type
);
1792 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
*self
, enum DevProbe type
);
1793 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
1795 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory
);
1798 static BOOL
MMDevApiLoad(void)
1800 static HRESULT InitResult
;
1804 InitResult
= E_FAIL
;
1806 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1807 if(req
.FinishedEvt
== NULL
)
1808 ERR("Failed to create event: %lu\n", GetLastError());
1811 ThreadHdl
= CreateThread(NULL
, 0, ALCmmdevProxy_messageHandler
, &req
, 0, &ThreadID
);
1812 if(ThreadHdl
!= NULL
)
1813 InitResult
= WaitForResponse(&req
);
1814 CloseHandle(req
.FinishedEvt
);
1817 return SUCCEEDED(InitResult
);
1820 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
* UNUSED(self
))
1822 VECTOR_INIT(PlaybackDevices
);
1823 VECTOR_INIT(CaptureDevices
);
1830 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
* UNUSED(self
))
1832 clear_devlist(&PlaybackDevices
);
1833 VECTOR_DEINIT(PlaybackDevices
);
1835 clear_devlist(&CaptureDevices
);
1836 VECTOR_DEINIT(CaptureDevices
);
1840 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
1841 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
1842 CloseHandle(ThreadHdl
);
1847 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1849 /* TODO: Disable capture with mmdevapi for now, since it doesn't do any
1850 * rechanneling or resampling; if the device is configured for 48000hz
1851 * stereo input, for example, and the app asks for 22050hz mono,
1852 * initialization will fail.
1854 if(type
== ALCbackend_Playback
/*|| type == ALCbackend_Capture*/)
1859 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
* UNUSED(self
), enum DevProbe type
)
1861 ThreadRequest req
= { NULL
, 0 };
1863 req
.FinishedEvt
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1864 if(req
.FinishedEvt
== NULL
)
1865 ERR("Failed to create event: %lu\n", GetLastError());
1868 HRESULT hr
= E_FAIL
;
1869 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
1870 hr
= WaitForResponse(&req
);
1871 if(SUCCEEDED(hr
)) switch(type
)
1873 case ALL_DEVICE_PROBE
:
1874 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1877 case CAPTURE_DEVICE_PROBE
:
1878 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1881 CloseHandle(req
.FinishedEvt
);
1882 req
.FinishedEvt
= NULL
;
1886 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1888 if(type
== ALCbackend_Playback
)
1890 ALCmmdevPlayback
*backend
;
1891 NEW_OBJ(backend
, ALCmmdevPlayback
)(device
);
1892 if(!backend
) return NULL
;
1893 return STATIC_CAST(ALCbackend
, backend
);
1895 if(type
== ALCbackend_Capture
)
1897 ALCmmdevCapture
*backend
;
1898 NEW_OBJ(backend
, ALCmmdevCapture
)(device
);
1899 if(!backend
) return NULL
;
1900 return STATIC_CAST(ALCbackend
, backend
);
1907 ALCbackendFactory
*ALCmmdevBackendFactory_getFactory(void)
1909 static ALCmmdevBackendFactory factory
= ALCMMDEVBACKENDFACTORY_INITIALIZER
;
1910 return STATIC_CAST(ALCbackendFactory
, &factory
);