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)
69 TYPEDEF_VECTOR(DevMap
, vector_DevMap
)
71 static void clear_devlist(vector_DevMap
*list
)
73 #define CLEAR_DEVMAP(i) do { \
74 AL_STRING_DEINIT((i)->name); \
78 VECTOR_FOR_EACH(DevMap
, *list
, CLEAR_DEVMAP
);
79 VECTOR_RESIZE(*list
, 0);
83 static vector_DevMap PlaybackDevices
;
84 static vector_DevMap CaptureDevices
;
87 static HANDLE ThreadHdl
;
88 static DWORD ThreadID
;
95 #define WM_USER_First (WM_USER+0)
96 #define WM_USER_OpenDevice (WM_USER+0)
97 #define WM_USER_ResetDevice (WM_USER+1)
98 #define WM_USER_StartDevice (WM_USER+2)
99 #define WM_USER_StopDevice (WM_USER+3)
100 #define WM_USER_CloseDevice (WM_USER+4)
101 #define WM_USER_Enumerate (WM_USER+5)
102 #define WM_USER_Last (WM_USER+5)
104 static inline void ReturnMsgResponse(ThreadRequest
*req
, HRESULT res
)
107 SetEvent(req
->FinishedEvt
);
110 static HRESULT
WaitForResponse(ThreadRequest
*req
)
112 if(WaitForSingleObject(req
->FinishedEvt
, INFINITE
) == WAIT_OBJECT_0
)
114 ERR("Message response error: %lu\n", GetLastError());
119 static void get_device_name(IMMDevice
*device
, al_string
*name
)
125 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
128 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
132 PropVariantInit(&pvname
);
134 hr
= IPropertyStore_GetValue(ps
, (const PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &pvname
);
136 WARN("GetValue Device_FriendlyName failed: 0x%08lx\n", hr
);
138 al_string_copy_wcstr(name
, pvname
.pwszVal
);
140 PropVariantClear(&pvname
);
141 IPropertyStore_Release(ps
);
144 static void get_device_formfactor(IMMDevice
*device
, EndpointFormFactor
*formfactor
)
150 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
153 WARN("OpenPropertyStore failed: 0x%08lx\n", hr
);
157 PropVariantInit(&pvform
);
159 hr
= IPropertyStore_GetValue(ps
, &PKEY_AudioEndpoint_FormFactor
, &pvform
);
161 WARN("GetValue AudioEndpoint_FormFactor failed: 0x%08lx\n", hr
);
163 *formfactor
= pvform
.ulVal
;
165 PropVariantClear(&pvform
);
166 IPropertyStore_Release(ps
);
170 static void add_device(IMMDevice
*device
, LPCWSTR devid
, vector_DevMap
*list
)
174 AL_STRING_INIT(entry
.name
);
175 entry
.devid
= strdupW(devid
);
176 get_device_name(device
, &entry
.name
);
178 TRACE("Got device \"%s\", \"%ls\"\n", al_string_get_cstr(entry
.name
), entry
.devid
);
179 VECTOR_PUSH_BACK(*list
, entry
);
182 static LPWSTR
get_device_id(IMMDevice
*device
)
187 hr
= IMMDevice_GetId(device
, &devid
);
190 ERR("Failed to get device id: %lx\n", hr
);
197 static HRESULT
probe_devices(IMMDeviceEnumerator
*devenum
, EDataFlow flowdir
, vector_DevMap
*list
)
199 IMMDeviceCollection
*coll
;
200 IMMDevice
*defdev
= NULL
;
201 LPWSTR defdevid
= NULL
;
206 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(devenum
, flowdir
, DEVICE_STATE_ACTIVE
, &coll
);
209 ERR("Failed to enumerate audio endpoints: 0x%08lx\n", hr
);
214 hr
= IMMDeviceCollection_GetCount(coll
, &count
);
215 if(SUCCEEDED(hr
) && count
> 0)
218 if(!VECTOR_RESERVE(*list
, count
))
220 IMMDeviceCollection_Release(coll
);
221 return E_OUTOFMEMORY
;
224 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum
, flowdir
,
225 eMultimedia
, &defdev
);
227 if(SUCCEEDED(hr
) && defdev
!= NULL
)
229 defdevid
= get_device_id(defdev
);
231 add_device(defdev
, defdevid
, list
);
234 for(i
= 0;i
< count
;++i
)
239 hr
= IMMDeviceCollection_Item(coll
, i
, &device
);
240 if(FAILED(hr
)) continue;
242 devid
= get_device_id(device
);
245 if(wcscmp(devid
, defdevid
) != 0)
246 add_device(device
, devid
, list
);
247 CoTaskMemFree(devid
);
249 IMMDevice_Release(device
);
252 if(defdev
) IMMDevice_Release(defdev
);
253 if(defdevid
) CoTaskMemFree(defdevid
);
254 IMMDeviceCollection_Release(coll
);
260 /* Proxy interface used by the message handler. */
261 struct ALCmmdevProxyVtable
;
263 typedef struct ALCmmdevProxy
{
264 const struct ALCmmdevProxyVtable
*vtbl
;
267 struct ALCmmdevProxyVtable
{
268 HRESULT (*const openProxy
)(ALCmmdevProxy
*);
269 void (*const closeProxy
)(ALCmmdevProxy
*);
271 HRESULT (*const resetProxy
)(ALCmmdevProxy
*);
272 HRESULT (*const startProxy
)(ALCmmdevProxy
*);
273 void (*const stopProxy
)(ALCmmdevProxy
*);
276 #define DEFINE_ALCMMDEVPROXY_VTABLE(T) \
277 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, openProxy) \
278 DECLARE_THUNK(T, ALCmmdevProxy, void, closeProxy) \
279 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, resetProxy) \
280 DECLARE_THUNK(T, ALCmmdevProxy, HRESULT, startProxy) \
281 DECLARE_THUNK(T, ALCmmdevProxy, void, stopProxy) \
283 static const struct ALCmmdevProxyVtable T##_ALCmmdevProxy_vtable = { \
284 T##_ALCmmdevProxy_openProxy, \
285 T##_ALCmmdevProxy_closeProxy, \
286 T##_ALCmmdevProxy_resetProxy, \
287 T##_ALCmmdevProxy_startProxy, \
288 T##_ALCmmdevProxy_stopProxy, \
291 static void ALCmmdevProxy_Construct(ALCmmdevProxy
* UNUSED(self
)) { }
292 static void ALCmmdevProxy_Destruct(ALCmmdevProxy
* UNUSED(self
)) { }
294 static DWORD CALLBACK
ALCmmdevProxy_messageHandler(void *ptr
)
296 ThreadRequest
*req
= ptr
;
297 IMMDeviceEnumerator
*Enumerator
;
298 ALuint deviceCount
= 0;
299 ALCmmdevProxy
*proxy
;
303 TRACE("Starting message thread\n");
305 cohr
= CoInitialize(NULL
);
308 WARN("Failed to initialize COM: 0x%08lx\n", cohr
);
309 ReturnMsgResponse(req
, cohr
);
313 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
316 WARN("Failed to create IMMDeviceEnumerator instance: 0x%08lx\n", hr
);
318 ReturnMsgResponse(req
, hr
);
322 IMMDeviceEnumerator_Release(Enumerator
);
327 /* HACK: Force Windows to create a message queue for this thread before
328 * returning success, otherwise PostThreadMessage may fail if it gets
329 * called before GetMessage.
331 PeekMessage(&msg
, NULL
, WM_USER
, WM_USER
, PM_NOREMOVE
);
333 TRACE("Message thread initialization complete\n");
334 ReturnMsgResponse(req
, S_OK
);
336 TRACE("Starting message loop\n");
337 while(GetMessage(&msg
, NULL
, WM_USER_First
, WM_USER_Last
))
339 TRACE("Got message %u\n", msg
.message
);
342 case WM_USER_OpenDevice
:
343 req
= (ThreadRequest
*)msg
.wParam
;
344 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
347 if(++deviceCount
== 1)
348 hr
= cohr
= CoInitialize(NULL
);
350 hr
= V0(proxy
,openProxy
)();
353 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
357 ReturnMsgResponse(req
, hr
);
360 case WM_USER_ResetDevice
:
361 req
= (ThreadRequest
*)msg
.wParam
;
362 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
364 hr
= V0(proxy
,resetProxy
)();
365 ReturnMsgResponse(req
, hr
);
368 case WM_USER_StartDevice
:
369 req
= (ThreadRequest
*)msg
.wParam
;
370 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
372 hr
= V0(proxy
,startProxy
)();
373 ReturnMsgResponse(req
, hr
);
376 case WM_USER_StopDevice
:
377 req
= (ThreadRequest
*)msg
.wParam
;
378 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
380 V0(proxy
,stopProxy
)();
381 ReturnMsgResponse(req
, S_OK
);
384 case WM_USER_CloseDevice
:
385 req
= (ThreadRequest
*)msg
.wParam
;
386 proxy
= (ALCmmdevProxy
*)msg
.lParam
;
388 V0(proxy
,closeProxy
)();
389 if(--deviceCount
== 0)
392 ReturnMsgResponse(req
, S_OK
);
395 case WM_USER_Enumerate
:
396 req
= (ThreadRequest
*)msg
.wParam
;
399 if(++deviceCount
== 1)
400 hr
= cohr
= CoInitialize(NULL
);
402 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
407 if(msg
.lParam
== ALL_DEVICE_PROBE
)
408 hr
= probe_devices(Enumerator
, eRender
, &PlaybackDevices
);
409 else if(msg
.lParam
== CAPTURE_DEVICE_PROBE
)
410 hr
= probe_devices(Enumerator
, eCapture
, &CaptureDevices
);
412 IMMDeviceEnumerator_Release(Enumerator
);
416 if(--deviceCount
== 0 && SUCCEEDED(cohr
))
419 ReturnMsgResponse(req
, hr
);
423 ERR("Unexpected message: %u\n", msg
.message
);
427 TRACE("Message loop finished\n");
433 typedef struct ALCmmdevPlayback
{
434 DERIVE_FROM_TYPE(ALCbackend
);
435 DERIVE_FROM_TYPE(ALCmmdevProxy
);
440 IAudioClient
*client
;
441 IAudioRenderClient
*render
;
446 volatile UINT32 Padding
;
448 volatile int killNow
;
452 static int ALCmmdevPlayback_mixerProc(void *arg
);
454 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
);
455 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
);
456 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*name
);
457 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
);
458 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
);
459 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
);
460 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
);
461 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
);
462 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
);
463 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
);
464 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
);
465 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
);
466 static DECLARE_FORWARD2(ALCmmdevPlayback
, ALCbackend
, ALCenum
, captureSamples
, ALCvoid
*, ALCuint
)
467 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, ALCuint
, availableSamples
)
468 static ALint64
ALCmmdevPlayback_getLatency(ALCmmdevPlayback
*self
);
469 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, lock
)
470 static DECLARE_FORWARD(ALCmmdevPlayback
, ALCbackend
, void, unlock
)
471 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevPlayback
)
473 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevPlayback
);
474 DEFINE_ALCBACKEND_VTABLE(ALCmmdevPlayback
);
477 static void ALCmmdevPlayback_Construct(ALCmmdevPlayback
*self
, ALCdevice
*device
)
479 SET_VTABLE2(ALCmmdevPlayback
, ALCbackend
, self
);
480 SET_VTABLE2(ALCmmdevPlayback
, ALCmmdevProxy
, self
);
481 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
482 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
489 self
->NotifyEvent
= NULL
;
491 self
->MsgEvent
= NULL
;
498 static void ALCmmdevPlayback_Destruct(ALCmmdevPlayback
*self
)
500 if(self
->NotifyEvent
!= NULL
)
501 CloseHandle(self
->NotifyEvent
);
502 self
->NotifyEvent
= NULL
;
503 if(self
->MsgEvent
!= NULL
)
504 CloseHandle(self
->MsgEvent
);
505 self
->MsgEvent
= NULL
;
510 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
511 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
515 FORCE_ALIGN
static int ALCmmdevPlayback_mixerProc(void *arg
)
517 ALCmmdevPlayback
*self
= arg
;
518 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
519 UINT32 buffer_len
, written
;
520 ALuint update_size
, len
;
524 hr
= CoInitialize(NULL
);
527 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
528 V0(device
->Backend
,lock
)();
529 aluHandleDisconnect(device
);
530 V0(device
->Backend
,unlock
)();
535 althrd_setname(althrd_current(), MIXER_THREAD_NAME
);
537 update_size
= device
->UpdateSize
;
538 buffer_len
= update_size
* device
->NumUpdates
;
539 while(!self
->killNow
)
541 hr
= IAudioClient_GetCurrentPadding(self
->client
, &written
);
544 ERR("Failed to get padding: 0x%08lx\n", hr
);
545 V0(device
->Backend
,lock
)();
546 aluHandleDisconnect(device
);
547 V0(device
->Backend
,unlock
)();
550 self
->Padding
= written
;
552 len
= buffer_len
- written
;
553 if(len
< update_size
)
556 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
557 if(res
!= WAIT_OBJECT_0
)
558 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
561 len
-= len
%update_size
;
563 hr
= IAudioRenderClient_GetBuffer(self
->render
, len
, &buffer
);
566 V0(device
->Backend
,lock
)();
567 aluMixData(device
, buffer
, len
);
568 self
->Padding
= written
+ len
;
569 V0(device
->Backend
,unlock
)();
570 hr
= IAudioRenderClient_ReleaseBuffer(self
->render
, len
, 0);
574 ERR("Failed to buffer data: 0x%08lx\n", hr
);
575 V0(device
->Backend
,lock
)();
576 aluHandleDisconnect(device
);
577 V0(device
->Backend
,unlock
)();
588 static ALCboolean
MakeExtensible(WAVEFORMATEXTENSIBLE
*out
, const WAVEFORMATEX
*in
)
590 memset(out
, 0, sizeof(*out
));
591 if(in
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
)
592 *out
= *(const WAVEFORMATEXTENSIBLE
*)in
;
593 else if(in
->wFormatTag
== WAVE_FORMAT_PCM
)
596 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
597 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
598 if(out
->Format
.nChannels
== 1)
599 out
->dwChannelMask
= MONO
;
600 else if(out
->Format
.nChannels
== 2)
601 out
->dwChannelMask
= STEREO
;
603 ERR("Unhandled PCM channel count: %d\n", out
->Format
.nChannels
);
604 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
606 else if(in
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
)
609 out
->Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
610 out
->Format
.cbSize
= sizeof(*out
) - sizeof(*in
);
611 if(out
->Format
.nChannels
== 1)
612 out
->dwChannelMask
= MONO
;
613 else if(out
->Format
.nChannels
== 2)
614 out
->dwChannelMask
= STEREO
;
616 ERR("Unhandled IEEE float channel count: %d\n", out
->Format
.nChannels
);
617 out
->SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
621 ERR("Unhandled format tag: 0x%04x\n", in
->wFormatTag
);
628 static ALCenum
ALCmmdevPlayback_open(ALCmmdevPlayback
*self
, const ALCchar
*deviceName
)
632 self
->NotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
633 self
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
634 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
636 ERR("Failed to create message events: %lu\n", GetLastError());
644 const DevMap
*iter
, *end
;
646 if(VECTOR_SIZE(PlaybackDevices
) == 0)
648 ThreadRequest req
= { self
->MsgEvent
, 0 };
649 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, ALL_DEVICE_PROBE
))
650 (void)WaitForResponse(&req
);
654 iter
= VECTOR_ITER_BEGIN(PlaybackDevices
);
655 end
= VECTOR_ITER_END(PlaybackDevices
);
656 for(;iter
!= end
;iter
++)
658 if(al_string_cmp_cstr(iter
->name
, deviceName
) == 0)
660 self
->devid
= strdupW(iter
->devid
);
666 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
672 ThreadRequest req
= { self
->MsgEvent
, 0 };
675 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
676 hr
= WaitForResponse(&req
);
678 ERR("Failed to post thread message: %lu\n", GetLastError());
683 if(self
->NotifyEvent
!= NULL
)
684 CloseHandle(self
->NotifyEvent
);
685 self
->NotifyEvent
= NULL
;
686 if(self
->MsgEvent
!= NULL
)
687 CloseHandle(self
->MsgEvent
);
688 self
->MsgEvent
= NULL
;
693 ERR("Device init failed: 0x%08lx\n", hr
);
694 return ALC_INVALID_VALUE
;
700 static HRESULT
ALCmmdevPlayback_openProxy(ALCmmdevPlayback
*self
)
702 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
706 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
709 IMMDeviceEnumerator
*Enumerator
= ptr
;
711 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eRender
, eMultimedia
, &self
->mmdev
);
713 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
714 IMMDeviceEnumerator_Release(Enumerator
);
718 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
722 get_device_name(self
->mmdev
, &device
->DeviceName
);
728 IMMDevice_Release(self
->mmdev
);
736 static void ALCmmdevPlayback_close(ALCmmdevPlayback
*self
)
738 ThreadRequest req
= { self
->MsgEvent
, 0 };
740 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
741 (void)WaitForResponse(&req
);
743 CloseHandle(self
->MsgEvent
);
744 self
->MsgEvent
= NULL
;
746 CloseHandle(self
->NotifyEvent
);
747 self
->NotifyEvent
= NULL
;
753 static void ALCmmdevPlayback_closeProxy(ALCmmdevPlayback
*self
)
756 IAudioClient_Release(self
->client
);
760 IMMDevice_Release(self
->mmdev
);
765 static ALCboolean
ALCmmdevPlayback_reset(ALCmmdevPlayback
*self
)
767 ThreadRequest req
= { self
->MsgEvent
, 0 };
770 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
771 hr
= WaitForResponse(&req
);
773 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
776 static HRESULT
ALCmmdevPlayback_resetProxy(ALCmmdevPlayback
*self
)
778 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
779 EndpointFormFactor formfactor
= UnknownFormFactor
;
780 WAVEFORMATEXTENSIBLE OutputType
;
781 WAVEFORMATEX
*wfx
= NULL
;
782 REFERENCE_TIME min_per
, buf_time
;
783 UINT32 buffer_len
, min_len
;
788 IAudioClient_Release(self
->client
);
791 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
794 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
799 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
802 ERR("Failed to get mix format: 0x%08lx\n", hr
);
806 if(!MakeExtensible(&OutputType
, wfx
))
814 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
815 device
->Frequency
-1) / device
->Frequency
;
817 if(!(device
->Flags
&DEVICE_FREQUENCY_REQUEST
))
818 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
819 if(!(device
->Flags
&DEVICE_CHANNELS_REQUEST
))
821 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
822 device
->FmtChans
= DevFmtMono
;
823 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
824 device
->FmtChans
= DevFmtStereo
;
825 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
826 device
->FmtChans
= DevFmtQuad
;
827 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
828 device
->FmtChans
= DevFmtX51
;
829 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
830 device
->FmtChans
= DevFmtX51Rear
;
831 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
832 device
->FmtChans
= DevFmtX61
;
833 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
834 device
->FmtChans
= DevFmtX71
;
836 ERR("Unhandled channel config: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
839 switch(device
->FmtChans
)
842 OutputType
.Format
.nChannels
= 1;
843 OutputType
.dwChannelMask
= MONO
;
845 case DevFmtBFormat3D
:
846 device
->FmtChans
= DevFmtStereo
;
849 OutputType
.Format
.nChannels
= 2;
850 OutputType
.dwChannelMask
= STEREO
;
853 OutputType
.Format
.nChannels
= 4;
854 OutputType
.dwChannelMask
= QUAD
;
857 OutputType
.Format
.nChannels
= 6;
858 OutputType
.dwChannelMask
= X5DOT1
;
861 OutputType
.Format
.nChannels
= 6;
862 OutputType
.dwChannelMask
= X5DOT1REAR
;
865 OutputType
.Format
.nChannels
= 7;
866 OutputType
.dwChannelMask
= X6DOT1
;
869 OutputType
.Format
.nChannels
= 8;
870 OutputType
.dwChannelMask
= X7DOT1
;
873 switch(device
->FmtType
)
876 device
->FmtType
= DevFmtUByte
;
879 OutputType
.Format
.wBitsPerSample
= 8;
880 OutputType
.Samples
.wValidBitsPerSample
= 8;
881 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
884 device
->FmtType
= DevFmtShort
;
887 OutputType
.Format
.wBitsPerSample
= 16;
888 OutputType
.Samples
.wValidBitsPerSample
= 16;
889 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
892 device
->FmtType
= DevFmtInt
;
895 OutputType
.Format
.wBitsPerSample
= 32;
896 OutputType
.Samples
.wValidBitsPerSample
= 32;
897 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
900 OutputType
.Format
.wBitsPerSample
= 32;
901 OutputType
.Samples
.wValidBitsPerSample
= 32;
902 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
905 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
907 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
908 OutputType
.Format
.wBitsPerSample
/ 8;
909 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
910 OutputType
.Format
.nBlockAlign
;
912 hr
= IAudioClient_IsFormatSupported(self
->client
, AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, &wfx
);
915 ERR("Failed to check format support: 0x%08lx\n", hr
);
916 hr
= IAudioClient_GetMixFormat(self
->client
, &wfx
);
920 ERR("Failed to find a supported format: 0x%08lx\n", hr
);
926 if(!MakeExtensible(&OutputType
, wfx
))
934 device
->Frequency
= OutputType
.Format
.nSamplesPerSec
;
935 if(OutputType
.Format
.nChannels
== 1 && OutputType
.dwChannelMask
== MONO
)
936 device
->FmtChans
= DevFmtMono
;
937 else if(OutputType
.Format
.nChannels
== 2 && OutputType
.dwChannelMask
== STEREO
)
938 device
->FmtChans
= DevFmtStereo
;
939 else if(OutputType
.Format
.nChannels
== 4 && OutputType
.dwChannelMask
== QUAD
)
940 device
->FmtChans
= DevFmtQuad
;
941 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1
)
942 device
->FmtChans
= DevFmtX51
;
943 else if(OutputType
.Format
.nChannels
== 6 && OutputType
.dwChannelMask
== X5DOT1REAR
)
944 device
->FmtChans
= DevFmtX51Rear
;
945 else if(OutputType
.Format
.nChannels
== 7 && OutputType
.dwChannelMask
== X6DOT1
)
946 device
->FmtChans
= DevFmtX61
;
947 else if(OutputType
.Format
.nChannels
== 8 && OutputType
.dwChannelMask
== X7DOT1
)
948 device
->FmtChans
= DevFmtX71
;
951 ERR("Unhandled extensible channels: %d -- 0x%08lx\n", OutputType
.Format
.nChannels
, OutputType
.dwChannelMask
);
952 device
->FmtChans
= DevFmtStereo
;
953 OutputType
.Format
.nChannels
= 2;
954 OutputType
.dwChannelMask
= STEREO
;
957 if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))
959 if(OutputType
.Format
.wBitsPerSample
== 8)
960 device
->FmtType
= DevFmtUByte
;
961 else if(OutputType
.Format
.wBitsPerSample
== 16)
962 device
->FmtType
= DevFmtShort
;
963 else if(OutputType
.Format
.wBitsPerSample
== 32)
964 device
->FmtType
= DevFmtInt
;
967 device
->FmtType
= DevFmtShort
;
968 OutputType
.Format
.wBitsPerSample
= 16;
971 else if(IsEqualGUID(&OutputType
.SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))
973 device
->FmtType
= DevFmtFloat
;
974 OutputType
.Format
.wBitsPerSample
= 32;
978 ERR("Unhandled format sub-type\n");
979 device
->FmtType
= DevFmtShort
;
980 OutputType
.Format
.wBitsPerSample
= 16;
981 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
983 OutputType
.Samples
.wValidBitsPerSample
= OutputType
.Format
.wBitsPerSample
;
985 get_device_formfactor(self
->mmdev
, &formfactor
);
986 device
->IsHeadphones
= (device
->FmtChans
== DevFmtStereo
&& formfactor
== Headphones
);
988 SetDefaultWFXChannelOrder(device
);
990 hr
= IAudioClient_Initialize(self
->client
, AUDCLNT_SHAREMODE_SHARED
,
991 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
992 buf_time
, 0, &OutputType
.Format
, NULL
);
995 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
999 hr
= IAudioClient_GetDevicePeriod(self
->client
, &min_per
, NULL
);
1002 min_len
= (UINT32
)((min_per
*device
->Frequency
+ 10000000-1) / 10000000);
1003 /* Find the nearest multiple of the period size to the update size */
1004 if(min_len
< device
->UpdateSize
)
1005 min_len
*= (device
->UpdateSize
+ min_len
/2)/min_len
;
1006 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1010 ERR("Failed to get audio buffer info: 0x%08lx\n", hr
);
1014 device
->UpdateSize
= min_len
;
1015 device
->NumUpdates
= buffer_len
/ device
->UpdateSize
;
1016 if(device
->NumUpdates
<= 1)
1018 ERR("Audio client returned buffer_len < period*2; expect break up\n");
1019 device
->NumUpdates
= 2;
1020 device
->UpdateSize
= buffer_len
/ device
->NumUpdates
;
1023 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1026 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1034 static ALCboolean
ALCmmdevPlayback_start(ALCmmdevPlayback
*self
)
1036 ThreadRequest req
= { self
->MsgEvent
, 0 };
1037 HRESULT hr
= E_FAIL
;
1039 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1040 hr
= WaitForResponse(&req
);
1042 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1045 static HRESULT
ALCmmdevPlayback_startProxy(ALCmmdevPlayback
*self
)
1050 ResetEvent(self
->NotifyEvent
);
1051 hr
= IAudioClient_Start(self
->client
);
1053 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1056 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioRenderClient
, &ptr
);
1061 if(althrd_create(&self
->thread
, ALCmmdevPlayback_mixerProc
, self
) != althrd_success
)
1064 IAudioRenderClient_Release(self
->render
);
1065 self
->render
= NULL
;
1066 IAudioClient_Stop(self
->client
);
1067 ERR("Failed to start thread\n");
1076 static void ALCmmdevPlayback_stop(ALCmmdevPlayback
*self
)
1078 ThreadRequest req
= { self
->MsgEvent
, 0 };
1079 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1080 (void)WaitForResponse(&req
);
1083 static void ALCmmdevPlayback_stopProxy(ALCmmdevPlayback
*self
)
1091 althrd_join(self
->thread
, &res
);
1093 IAudioRenderClient_Release(self
->render
);
1094 self
->render
= NULL
;
1095 IAudioClient_Stop(self
->client
);
1099 static ALint64
ALCmmdevPlayback_getLatency(ALCmmdevPlayback
*self
)
1101 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1102 return (ALint64
)self
->Padding
* 1000000000 / device
->Frequency
;
1106 typedef struct ALCmmdevCapture
{
1107 DERIVE_FROM_TYPE(ALCbackend
);
1108 DERIVE_FROM_TYPE(ALCmmdevProxy
);
1113 IAudioClient
*client
;
1114 IAudioCaptureClient
*capture
;
1121 volatile int killNow
;
1125 static int ALCmmdevCapture_recordProc(void *arg
);
1127 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
);
1128 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
);
1129 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*name
);
1130 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
);
1131 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
);
1132 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
);
1133 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ALCboolean
, reset
)
1134 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
);
1135 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
);
1136 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
);
1137 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
);
1138 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
);
1139 static ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
);
1140 static ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
);
1141 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, ALint64
, getLatency
)
1142 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, lock
)
1143 static DECLARE_FORWARD(ALCmmdevCapture
, ALCbackend
, void, unlock
)
1144 DECLARE_DEFAULT_ALLOCATORS(ALCmmdevCapture
)
1146 DEFINE_ALCMMDEVPROXY_VTABLE(ALCmmdevCapture
);
1147 DEFINE_ALCBACKEND_VTABLE(ALCmmdevCapture
);
1150 static void ALCmmdevCapture_Construct(ALCmmdevCapture
*self
, ALCdevice
*device
)
1152 SET_VTABLE2(ALCmmdevCapture
, ALCbackend
, self
);
1153 SET_VTABLE2(ALCmmdevCapture
, ALCmmdevProxy
, self
);
1154 ALCbackend_Construct(STATIC_CAST(ALCbackend
, self
), device
);
1155 ALCmmdevProxy_Construct(STATIC_CAST(ALCmmdevProxy
, self
));
1160 self
->client
= NULL
;
1161 self
->capture
= NULL
;
1162 self
->NotifyEvent
= NULL
;
1164 self
->MsgEvent
= NULL
;
1171 static void ALCmmdevCapture_Destruct(ALCmmdevCapture
*self
)
1173 DestroyRingBuffer(self
->Ring
);
1176 if(self
->NotifyEvent
!= NULL
)
1177 CloseHandle(self
->NotifyEvent
);
1178 self
->NotifyEvent
= NULL
;
1179 if(self
->MsgEvent
!= NULL
)
1180 CloseHandle(self
->MsgEvent
);
1181 self
->MsgEvent
= NULL
;
1186 ALCmmdevProxy_Destruct(STATIC_CAST(ALCmmdevProxy
, self
));
1187 ALCbackend_Destruct(STATIC_CAST(ALCbackend
, self
));
1191 FORCE_ALIGN
int ALCmmdevCapture_recordProc(void *arg
)
1193 ALCmmdevCapture
*self
= arg
;
1194 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1197 hr
= CoInitialize(NULL
);
1200 ERR("CoInitialize(NULL) failed: 0x%08lx\n", hr
);
1201 V0(device
->Backend
,lock
)();
1202 aluHandleDisconnect(device
);
1203 V0(device
->Backend
,unlock
)();
1207 althrd_setname(althrd_current(), RECORD_THREAD_NAME
);
1209 while(!self
->killNow
)
1214 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1216 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1217 else while(avail
> 0 && SUCCEEDED(hr
))
1219 UINT32 flags
, numsamples
;
1222 hr
= IAudioCaptureClient_GetBuffer(self
->capture
,
1223 &data
, &numsamples
, &flags
, NULL
, NULL
1227 ERR("Failed to get capture buffer: 0x%08lx\n", hr
);
1231 WriteRingBuffer(self
->Ring
, data
, numsamples
);
1233 hr
= IAudioCaptureClient_ReleaseBuffer(self
->capture
, numsamples
);
1236 ERR("Failed to release capture buffer: 0x%08lx\n", hr
);
1240 hr
= IAudioCaptureClient_GetNextPacketSize(self
->capture
, &avail
);
1242 ERR("Failed to get next packet size: 0x%08lx\n", hr
);
1247 V0(device
->Backend
,lock
)();
1248 aluHandleDisconnect(device
);
1249 V0(device
->Backend
,unlock
)();
1253 res
= WaitForSingleObjectEx(self
->NotifyEvent
, 2000, FALSE
);
1254 if(res
!= WAIT_OBJECT_0
)
1255 ERR("WaitForSingleObjectEx error: 0x%lx\n", res
);
1263 static ALCenum
ALCmmdevCapture_open(ALCmmdevCapture
*self
, const ALCchar
*deviceName
)
1267 self
->NotifyEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1268 self
->MsgEvent
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1269 if(self
->NotifyEvent
== NULL
|| self
->MsgEvent
== NULL
)
1271 ERR("Failed to create message events: %lu\n", GetLastError());
1281 if(VECTOR_SIZE(CaptureDevices
) == 0)
1283 ThreadRequest req
= { self
->MsgEvent
, 0 };
1284 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, CAPTURE_DEVICE_PROBE
))
1285 (void)WaitForResponse(&req
);
1289 #define MATCH_NAME(i) (al_string_cmp_cstr((i)->name, deviceName) == 0)
1290 VECTOR_FIND_IF(iter
, const DevMap
, CaptureDevices
, MATCH_NAME
);
1291 if(iter
== VECTOR_ITER_END(CaptureDevices
))
1292 WARN("Failed to find device name matching \"%s\"\n", deviceName
);
1295 self
->devid
= strdupW(iter
->devid
);
1304 ThreadRequest req
= { self
->MsgEvent
, 0 };
1307 if(PostThreadMessage(ThreadID
, WM_USER_OpenDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1308 hr
= WaitForResponse(&req
);
1310 ERR("Failed to post thread message: %lu\n", GetLastError());
1315 if(self
->NotifyEvent
!= NULL
)
1316 CloseHandle(self
->NotifyEvent
);
1317 self
->NotifyEvent
= NULL
;
1318 if(self
->MsgEvent
!= NULL
)
1319 CloseHandle(self
->MsgEvent
);
1320 self
->MsgEvent
= NULL
;
1325 ERR("Device init failed: 0x%08lx\n", hr
);
1326 return ALC_INVALID_VALUE
;
1330 ThreadRequest req
= { self
->MsgEvent
, 0 };
1333 if(PostThreadMessage(ThreadID
, WM_USER_ResetDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1334 hr
= WaitForResponse(&req
);
1336 ERR("Failed to post thread message: %lu\n", GetLastError());
1340 ALCmmdevCapture_close(self
);
1341 if(hr
== E_OUTOFMEMORY
)
1342 return ALC_OUT_OF_MEMORY
;
1343 return ALC_INVALID_VALUE
;
1347 return ALC_NO_ERROR
;
1350 static HRESULT
ALCmmdevCapture_openProxy(ALCmmdevCapture
*self
)
1352 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1356 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, &ptr
);
1359 IMMDeviceEnumerator
*Enumerator
= ptr
;
1361 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(Enumerator
, eCapture
, eMultimedia
, &self
->mmdev
);
1363 hr
= IMMDeviceEnumerator_GetDevice(Enumerator
, self
->devid
, &self
->mmdev
);
1364 IMMDeviceEnumerator_Release(Enumerator
);
1368 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1372 get_device_name(self
->mmdev
, &device
->DeviceName
);
1378 IMMDevice_Release(self
->mmdev
);
1386 static void ALCmmdevCapture_close(ALCmmdevCapture
*self
)
1388 ThreadRequest req
= { self
->MsgEvent
, 0 };
1390 if(PostThreadMessage(ThreadID
, WM_USER_CloseDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1391 (void)WaitForResponse(&req
);
1393 DestroyRingBuffer(self
->Ring
);
1396 CloseHandle(self
->MsgEvent
);
1397 self
->MsgEvent
= NULL
;
1399 CloseHandle(self
->NotifyEvent
);
1400 self
->NotifyEvent
= NULL
;
1406 static void ALCmmdevCapture_closeProxy(ALCmmdevCapture
*self
)
1409 IAudioClient_Release(self
->client
);
1410 self
->client
= NULL
;
1413 IMMDevice_Release(self
->mmdev
);
1418 static HRESULT
ALCmmdevCapture_resetProxy(ALCmmdevCapture
*self
)
1420 ALCdevice
*device
= STATIC_CAST(ALCbackend
, self
)->mDevice
;
1421 WAVEFORMATEXTENSIBLE OutputType
;
1422 REFERENCE_TIME buf_time
;
1428 IAudioClient_Release(self
->client
);
1429 self
->client
= NULL
;
1431 hr
= IMMDevice_Activate(self
->mmdev
, &IID_IAudioClient
, CLSCTX_INPROC_SERVER
, NULL
, &ptr
);
1434 ERR("Failed to reactivate audio client: 0x%08lx\n", hr
);
1439 buf_time
= ((REFERENCE_TIME
)device
->UpdateSize
*device
->NumUpdates
*10000000 +
1440 device
->Frequency
-1) / device
->Frequency
;
1442 OutputType
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
1443 switch(device
->FmtChans
)
1446 OutputType
.Format
.nChannels
= 1;
1447 OutputType
.dwChannelMask
= MONO
;
1450 OutputType
.Format
.nChannels
= 2;
1451 OutputType
.dwChannelMask
= STEREO
;
1454 OutputType
.Format
.nChannels
= 4;
1455 OutputType
.dwChannelMask
= QUAD
;
1458 OutputType
.Format
.nChannels
= 6;
1459 OutputType
.dwChannelMask
= X5DOT1
;
1462 OutputType
.Format
.nChannels
= 6;
1463 OutputType
.dwChannelMask
= X5DOT1REAR
;
1466 OutputType
.Format
.nChannels
= 7;
1467 OutputType
.dwChannelMask
= X6DOT1
;
1470 OutputType
.Format
.nChannels
= 8;
1471 OutputType
.dwChannelMask
= X7DOT1
;
1474 case DevFmtBFormat3D
:
1477 switch(device
->FmtType
)
1480 OutputType
.Format
.wBitsPerSample
= 8;
1481 OutputType
.Samples
.wValidBitsPerSample
= 8;
1482 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1485 OutputType
.Format
.wBitsPerSample
= 16;
1486 OutputType
.Samples
.wValidBitsPerSample
= 16;
1487 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1490 OutputType
.Format
.wBitsPerSample
= 32;
1491 OutputType
.Samples
.wValidBitsPerSample
= 32;
1492 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_PCM
;
1495 OutputType
.Format
.wBitsPerSample
= 32;
1496 OutputType
.Samples
.wValidBitsPerSample
= 32;
1497 OutputType
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
1503 WARN("%s capture samples not supported\n", DevFmtTypeString(device
->FmtType
));
1506 OutputType
.Format
.nSamplesPerSec
= device
->Frequency
;
1508 OutputType
.Format
.nBlockAlign
= OutputType
.Format
.nChannels
*
1509 OutputType
.Format
.wBitsPerSample
/ 8;
1510 OutputType
.Format
.nAvgBytesPerSec
= OutputType
.Format
.nSamplesPerSec
*
1511 OutputType
.Format
.nBlockAlign
;
1513 hr
= IAudioClient_IsFormatSupported(self
->client
,
1514 AUDCLNT_SHAREMODE_SHARED
, &OutputType
.Format
, NULL
1518 ERR("Failed to check format support: 0x%08lx\n", hr
);
1522 hr
= IAudioClient_Initialize(self
->client
,
1523 AUDCLNT_SHAREMODE_SHARED
, AUDCLNT_STREAMFLAGS_EVENTCALLBACK
,
1524 buf_time
, 0, &OutputType
.Format
, NULL
1528 ERR("Failed to initialize audio client: 0x%08lx\n", hr
);
1532 hr
= IAudioClient_GetBufferSize(self
->client
, &buffer_len
);
1535 ERR("Failed to get buffer size: 0x%08lx\n", hr
);
1539 buffer_len
= maxu(device
->UpdateSize
*device
->NumUpdates
, buffer_len
);
1540 self
->Ring
= CreateRingBuffer(OutputType
.Format
.nBlockAlign
, buffer_len
);
1543 ERR("Failed to allocate capture ring buffer\n");
1544 return E_OUTOFMEMORY
;
1547 hr
= IAudioClient_SetEventHandle(self
->client
, self
->NotifyEvent
);
1550 ERR("Failed to set event handle: 0x%08lx\n", hr
);
1558 static ALCboolean
ALCmmdevCapture_start(ALCmmdevCapture
*self
)
1560 ThreadRequest req
= { self
->MsgEvent
, 0 };
1561 HRESULT hr
= E_FAIL
;
1563 if(PostThreadMessage(ThreadID
, WM_USER_StartDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1564 hr
= WaitForResponse(&req
);
1566 return SUCCEEDED(hr
) ? ALC_TRUE
: ALC_FALSE
;
1569 static HRESULT
ALCmmdevCapture_startProxy(ALCmmdevCapture
*self
)
1574 ResetEvent(self
->NotifyEvent
);
1575 hr
= IAudioClient_Start(self
->client
);
1578 ERR("Failed to start audio client: 0x%08lx\n", hr
);
1582 hr
= IAudioClient_GetService(self
->client
, &IID_IAudioCaptureClient
, &ptr
);
1585 self
->capture
= ptr
;
1587 if(althrd_create(&self
->thread
, ALCmmdevCapture_recordProc
, self
) != althrd_success
)
1589 ERR("Failed to start thread\n");
1590 IAudioCaptureClient_Release(self
->capture
);
1591 self
->capture
= NULL
;
1598 IAudioClient_Stop(self
->client
);
1599 IAudioClient_Reset(self
->client
);
1606 static void ALCmmdevCapture_stop(ALCmmdevCapture
*self
)
1608 ThreadRequest req
= { self
->MsgEvent
, 0 };
1609 if(PostThreadMessage(ThreadID
, WM_USER_StopDevice
, (WPARAM
)&req
, (LPARAM
)STATIC_CAST(ALCmmdevProxy
, self
)))
1610 (void)WaitForResponse(&req
);
1613 static void ALCmmdevCapture_stopProxy(ALCmmdevCapture
*self
)
1621 althrd_join(self
->thread
, &res
);
1623 IAudioCaptureClient_Release(self
->capture
);
1624 self
->capture
= NULL
;
1625 IAudioClient_Stop(self
->client
);
1626 IAudioClient_Reset(self
->client
);
1630 ALuint
ALCmmdevCapture_availableSamples(ALCmmdevCapture
*self
)
1632 return RingBufferSize(self
->Ring
);
1635 ALCenum
ALCmmdevCapture_captureSamples(ALCmmdevCapture
*self
, ALCvoid
*buffer
, ALCuint samples
)
1637 if(ALCmmdevCapture_availableSamples(self
) < samples
)
1638 return ALC_INVALID_VALUE
;
1639 ReadRingBuffer(self
->Ring
, buffer
, samples
);
1640 return ALC_NO_ERROR
;
1644 static inline void AppendAllDevicesList2(const DevMap
*entry
)
1645 { AppendAllDevicesList(al_string_get_cstr(entry
->name
)); }
1646 static inline void AppendCaptureDeviceList2(const DevMap
*entry
)
1647 { AppendCaptureDeviceList(al_string_get_cstr(entry
->name
)); }
1649 typedef struct ALCmmdevBackendFactory
{
1650 DERIVE_FROM_TYPE(ALCbackendFactory
);
1651 } ALCmmdevBackendFactory
;
1652 #define ALCMMDEVBACKENDFACTORY_INITIALIZER { { GET_VTABLE2(ALCmmdevBackendFactory, ALCbackendFactory) } }
1654 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
*self
);
1655 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
*self
);
1656 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
*self
, ALCbackend_Type type
);
1657 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
*self
, enum DevProbe type
);
1658 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
*self
, ALCdevice
*device
, ALCbackend_Type type
);
1660 DEFINE_ALCBACKENDFACTORY_VTABLE(ALCmmdevBackendFactory
);
1663 static BOOL
MMDevApiLoad(void)
1665 static HRESULT InitResult
;
1669 InitResult
= E_FAIL
;
1671 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1672 if(req
.FinishedEvt
== NULL
)
1673 ERR("Failed to create event: %lu\n", GetLastError());
1676 ThreadHdl
= CreateThread(NULL
, 0, ALCmmdevProxy_messageHandler
, &req
, 0, &ThreadID
);
1677 if(ThreadHdl
!= NULL
)
1678 InitResult
= WaitForResponse(&req
);
1679 CloseHandle(req
.FinishedEvt
);
1682 return SUCCEEDED(InitResult
);
1685 static ALCboolean
ALCmmdevBackendFactory_init(ALCmmdevBackendFactory
* UNUSED(self
))
1687 VECTOR_INIT(PlaybackDevices
);
1688 VECTOR_INIT(CaptureDevices
);
1695 static void ALCmmdevBackendFactory_deinit(ALCmmdevBackendFactory
* UNUSED(self
))
1697 clear_devlist(&PlaybackDevices
);
1698 VECTOR_DEINIT(PlaybackDevices
);
1700 clear_devlist(&CaptureDevices
);
1701 VECTOR_DEINIT(CaptureDevices
);
1705 TRACE("Sending WM_QUIT to Thread %04lx\n", ThreadID
);
1706 PostThreadMessage(ThreadID
, WM_QUIT
, 0, 0);
1707 CloseHandle(ThreadHdl
);
1712 static ALCboolean
ALCmmdevBackendFactory_querySupport(ALCmmdevBackendFactory
* UNUSED(self
), ALCbackend_Type type
)
1714 if(type
== ALCbackend_Playback
|| type
== ALCbackend_Capture
)
1719 static void ALCmmdevBackendFactory_probe(ALCmmdevBackendFactory
* UNUSED(self
), enum DevProbe type
)
1721 ThreadRequest req
= { NULL
, 0 };
1723 req
.FinishedEvt
= CreateEvent(NULL
, FALSE
, FALSE
, NULL
);
1724 if(req
.FinishedEvt
== NULL
)
1725 ERR("Failed to create event: %lu\n", GetLastError());
1728 HRESULT hr
= E_FAIL
;
1729 if(PostThreadMessage(ThreadID
, WM_USER_Enumerate
, (WPARAM
)&req
, type
))
1730 hr
= WaitForResponse(&req
);
1731 if(SUCCEEDED(hr
)) switch(type
)
1733 case ALL_DEVICE_PROBE
:
1734 VECTOR_FOR_EACH(const DevMap
, PlaybackDevices
, AppendAllDevicesList2
);
1737 case CAPTURE_DEVICE_PROBE
:
1738 VECTOR_FOR_EACH(const DevMap
, CaptureDevices
, AppendCaptureDeviceList2
);
1741 CloseHandle(req
.FinishedEvt
);
1742 req
.FinishedEvt
= NULL
;
1746 static ALCbackend
* ALCmmdevBackendFactory_createBackend(ALCmmdevBackendFactory
* UNUSED(self
), ALCdevice
*device
, ALCbackend_Type type
)
1748 if(type
== ALCbackend_Playback
)
1750 ALCmmdevPlayback
*backend
;
1752 backend
= ALCmmdevPlayback_New(sizeof(*backend
));
1753 if(!backend
) return NULL
;
1754 memset(backend
, 0, sizeof(*backend
));
1756 ALCmmdevPlayback_Construct(backend
, device
);
1758 return STATIC_CAST(ALCbackend
, backend
);
1760 if(type
== ALCbackend_Capture
)
1762 ALCmmdevCapture
*backend
;
1764 backend
= ALCmmdevCapture_New(sizeof(*backend
));
1765 if(!backend
) return NULL
;
1766 memset(backend
, 0, sizeof(*backend
));
1768 ALCmmdevCapture_Construct(backend
, device
);
1770 return STATIC_CAST(ALCbackend
, backend
);
1777 ALCbackendFactory
*ALCmmdevBackendFactory_getFactory(void)
1779 static ALCmmdevBackendFactory factory
= ALCMMDEVBACKENDFACTORY_INITIALIZER
;
1780 return STATIC_CAST(ALCbackendFactory
, &factory
);