2 * Copyright 2011-2012 Maarten Lankhorst
3 * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
4 * Copyright 2011 Andrew Eikum for CodeWeavers
5 * Copyright 2022 Huw Davies
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <audiopolicy.h>
27 #include <mmdeviceapi.h>
30 #include <wine/debug.h>
31 #include <wine/unixlib.h>
33 #include "mmdevapi_private.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
37 typedef struct tagLANGANDCODEPAGE
43 extern void sessions_lock(void) DECLSPEC_HIDDEN
;
44 extern void sessions_unlock(void) DECLSPEC_HIDDEN
;
46 extern HRESULT
get_audio_session(const GUID
*sessionguid
, IMMDevice
*device
, UINT channels
,
47 struct audio_session
**out
) DECLSPEC_HIDDEN
;
48 extern struct audio_session_wrapper
*session_wrapper_create(struct audio_client
*client
) DECLSPEC_HIDDEN
;
50 static HANDLE main_loop_thread
;
52 void main_loop_stop(void)
54 if (main_loop_thread
) {
55 WaitForSingleObject(main_loop_thread
, INFINITE
);
56 CloseHandle(main_loop_thread
);
60 void set_stream_volumes(struct audio_client
*This
)
62 struct set_volumes_params params
;
64 params
.stream
= This
->stream
;
65 params
.master_volume
= (This
->session
->mute
? 0.0f
: This
->session
->master_vol
);
66 params
.volumes
= This
->vols
;
67 params
.session_volumes
= This
->session
->channel_vols
;
69 wine_unix_call(set_volumes
, ¶ms
);
72 static inline struct audio_client
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
74 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioCaptureClient_iface
);
77 static inline struct audio_client
*impl_from_IAudioClient3(IAudioClient3
*iface
)
79 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioClient3_iface
);
82 static inline struct audio_client
*impl_from_IAudioClock(IAudioClock
*iface
)
84 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioClock_iface
);
87 static inline struct audio_client
*impl_from_IAudioClock2(IAudioClock2
*iface
)
89 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioClock2_iface
);
92 static inline struct audio_client
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
94 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioRenderClient_iface
);
97 static inline struct audio_client
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
99 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioStreamVolume_iface
);
102 static void dump_fmt(const WAVEFORMATEX
*fmt
)
104 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
105 switch (fmt
->wFormatTag
) {
106 case WAVE_FORMAT_PCM
:
107 TRACE("WAVE_FORMAT_PCM");
109 case WAVE_FORMAT_IEEE_FLOAT
:
110 TRACE("WAVE_FORMAT_IEEE_FLOAT");
112 case WAVE_FORMAT_EXTENSIBLE
:
113 TRACE("WAVE_FORMAT_EXTENSIBLE");
121 TRACE("nChannels: %u\n", fmt
->nChannels
);
122 TRACE("nSamplesPerSec: %lu\n", fmt
->nSamplesPerSec
);
123 TRACE("nAvgBytesPerSec: %lu\n", fmt
->nAvgBytesPerSec
);
124 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
125 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
126 TRACE("cbSize: %u\n", fmt
->cbSize
);
128 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
129 WAVEFORMATEXTENSIBLE
*fmtex
= (void *)fmt
;
130 TRACE("dwChannelMask: %08lx\n", fmtex
->dwChannelMask
);
131 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
132 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
136 static DWORD CALLBACK
main_loop_func(void *event
)
138 struct main_loop_params params
;
140 SetThreadDescription(GetCurrentThread(), L
"audio_client_main");
142 params
.event
= event
;
144 wine_unix_call(main_loop
, ¶ms
);
149 HRESULT
main_loop_start(void)
151 if (!main_loop_thread
) {
152 HANDLE event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
153 if (!(main_loop_thread
= CreateThread(NULL
, 0, main_loop_func
, event
, 0, NULL
))) {
154 ERR("Failed to create main loop thread\n");
159 SetThreadPriority(main_loop_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
160 WaitForSingleObject(event
, INFINITE
);
167 static DWORD CALLBACK
timer_loop_func(void *user
)
169 struct timer_loop_params params
;
170 struct audio_client
*This
= user
;
172 SetThreadDescription(GetCurrentThread(), L
"audio_client_timer");
174 params
.stream
= This
->stream
;
176 wine_unix_call(timer_loop
, ¶ms
);
181 HRESULT
stream_release(stream_handle stream
, HANDLE timer_thread
)
183 struct release_stream_params params
;
185 params
.stream
= stream
;
186 params
.timer_thread
= timer_thread
;
188 wine_unix_call(release_stream
, ¶ms
);
190 return params
.result
;
193 static BOOL
query_productname(void *data
, LANGANDCODEPAGE
*lang
, LPVOID
*buffer
, UINT
*len
)
196 swprintf(pn
, ARRAY_SIZE(pn
), L
"\\StringFileInfo\\%04x%04x\\ProductName", lang
->wLanguage
, lang
->wCodePage
);
197 return VerQueryValueW(data
, pn
, buffer
, len
) && *len
;
200 WCHAR
*get_application_name(void)
202 WCHAR path
[MAX_PATH
], *name
;
203 UINT translate_size
, productname_size
;
204 LANGANDCODEPAGE
*translate
;
212 GetModuleFileNameW(NULL
, path
, ARRAY_SIZE(path
));
214 size
= GetFileVersionInfoSizeW(path
, NULL
);
222 if (!GetFileVersionInfoW(path
, 0, size
, data
))
225 if (!VerQueryValueW(data
, L
"\\VarFileInfo\\Translation", (LPVOID
*)&translate
, &translate_size
))
228 /* No translations found. */
229 if (translate_size
< sizeof(LANGANDCODEPAGE
))
232 /* The following code will try to find the best translation. We first search for an
233 * exact match of the language, then a match of the language PRIMARYLANGID, then we
234 * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the
235 * first entry which contains a proper productname. */
236 locale
= GetThreadLocale();
238 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
239 if (translate
[i
].wLanguage
== locale
&&
240 query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
247 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
248 if (PRIMARYLANGID(translate
[i
].wLanguage
) == PRIMARYLANGID(locale
) &&
249 query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
257 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
258 if (PRIMARYLANGID(translate
[i
].wLanguage
) == LANG_NEUTRAL
&&
259 query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
267 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
268 if (query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
276 name
= wcsdup(productname
);
283 name
= wcsrchr(path
, '\\');
292 static HRESULT WINAPI
capture_QueryInterface(IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
294 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
296 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
301 if (IsEqualIID(riid
, &IID_IUnknown
) ||
302 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
304 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
305 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
308 return E_NOINTERFACE
;
311 IUnknown_AddRef((IUnknown
*)*ppv
);
316 static ULONG WINAPI
capture_AddRef(IAudioCaptureClient
*iface
)
318 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
319 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
322 static ULONG WINAPI
capture_Release(IAudioCaptureClient
*iface
)
324 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
325 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
328 static HRESULT WINAPI
capture_GetBuffer(IAudioCaptureClient
*iface
, BYTE
**data
, UINT32
*frames
,
329 DWORD
*flags
, UINT64
*devpos
, UINT64
*qpcpos
)
331 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
332 struct get_capture_buffer_params params
;
334 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
, devpos
, qpcpos
);
341 if (!frames
|| !flags
)
345 return AUDCLNT_E_NOT_INITIALIZED
;
347 params
.stream
= This
->stream
;
349 params
.frames
= frames
;
350 params
.flags
= (UINT
*)flags
;
351 params
.devpos
= devpos
;
352 params
.qpcpos
= qpcpos
;
354 wine_unix_call(get_capture_buffer
, ¶ms
);
356 return params
.result
;
359 static HRESULT WINAPI
capture_ReleaseBuffer(IAudioCaptureClient
*iface
, UINT32 done
)
361 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
362 struct release_capture_buffer_params params
;
364 TRACE("(%p)->(%u)\n", This
, done
);
367 return AUDCLNT_E_NOT_INITIALIZED
;
369 params
.stream
= This
->stream
;
372 wine_unix_call(release_capture_buffer
, ¶ms
);
374 return params
.result
;
377 static HRESULT WINAPI
capture_GetNextPacketSize(IAudioCaptureClient
*iface
, UINT32
*frames
)
379 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
380 struct get_next_packet_size_params params
;
382 TRACE("(%p)->(%p)\n", This
, frames
);
388 return AUDCLNT_E_NOT_INITIALIZED
;
390 params
.stream
= This
->stream
;
391 params
.frames
= frames
;
393 wine_unix_call(get_next_packet_size
, ¶ms
);
395 return params
.result
;
398 const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
400 capture_QueryInterface
,
404 capture_ReleaseBuffer
,
405 capture_GetNextPacketSize
408 static HRESULT WINAPI
client_QueryInterface(IAudioClient3
*iface
, REFIID riid
, void **ppv
)
410 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
415 if (IsEqualIID(riid
, &IID_IUnknown
) ||
416 IsEqualIID(riid
, &IID_IAudioClient
) ||
417 IsEqualIID(riid
, &IID_IAudioClient2
) ||
418 IsEqualIID(riid
, &IID_IAudioClient3
))
420 else if(IsEqualIID(riid
, &IID_IMarshal
)) {
421 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
422 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
425 return E_NOINTERFACE
;
428 IUnknown_AddRef((IUnknown
*)*ppv
);
433 static ULONG WINAPI
client_AddRef(IAudioClient3
*iface
)
435 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
436 ULONG ref
= InterlockedIncrement(&This
->ref
);
437 TRACE("(%p) Refcount now %lu\n", This
, ref
);
441 static ULONG WINAPI
client_Release(IAudioClient3
*iface
)
443 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
444 ULONG ref
= InterlockedDecrement(&This
->ref
);
445 TRACE("(%p) Refcount now %lu\n", This
, ref
);
448 IAudioClient3_Stop(iface
);
449 IMMDevice_Release(This
->parent
);
450 IUnknown_Release(This
->marshal
);
454 list_remove(&This
->entry
);
461 stream_release(This
->stream
, This
->timer_thread
);
469 static HRESULT WINAPI
client_Initialize(IAudioClient3
*iface
, AUDCLNT_SHAREMODE mode
, DWORD flags
,
470 REFERENCE_TIME duration
, REFERENCE_TIME period
,
471 const WAVEFORMATEX
*fmt
, const GUID
*sessionguid
)
473 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
474 struct create_stream_params params
;
475 UINT32 i
, channel_count
;
476 stream_handle stream
;
479 TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This
, mode
, flags
, wine_dbgstr_longlong(duration
),
480 wine_dbgstr_longlong(period
), fmt
,
481 debugstr_guid(sessionguid
));
488 if (mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
491 if (flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
492 AUDCLNT_STREAMFLAGS_LOOPBACK
|
493 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
494 AUDCLNT_STREAMFLAGS_NOPERSIST
|
495 AUDCLNT_STREAMFLAGS_RATEADJUST
|
496 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
497 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
498 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
|
499 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
500 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
)) {
501 FIXME("Unknown flags: %08lx\n", flags
);
509 return AUDCLNT_E_ALREADY_INITIALIZED
;
512 if (FAILED(params
.result
= main_loop_start())) {
514 return params
.result
;
517 params
.name
= name
= get_application_name();
518 params
.device
= This
->device_name
;
519 params
.flow
= This
->dataflow
;
521 params
.flags
= flags
;
522 params
.duration
= duration
;
523 params
.period
= period
;
525 params
.channel_count
= &channel_count
;
526 params
.stream
= &stream
;
528 wine_unix_call(create_stream
, ¶ms
);
532 if (FAILED(params
.result
)) {
534 return params
.result
;
537 if (!(This
->vols
= malloc(channel_count
* sizeof(*This
->vols
)))) {
538 params
.result
= E_OUTOFMEMORY
;
542 for (i
= 0; i
< channel_count
; i
++)
545 params
.result
= get_audio_session(sessionguid
, This
->parent
, channel_count
, &This
->session
);
548 if (FAILED(params
.result
)) {
549 stream_release(stream
, NULL
);
553 list_add_tail(&This
->session
->clients
, &This
->entry
);
554 This
->stream
= stream
;
555 This
->channel_count
= channel_count
;
556 set_stream_volumes(This
);
561 return params
.result
;
564 static HRESULT WINAPI
client_GetBufferSize(IAudioClient3
*iface
, UINT32
*out
)
566 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
567 struct get_buffer_size_params params
;
569 TRACE("(%p)->(%p)\n", This
, out
);
575 return AUDCLNT_E_NOT_INITIALIZED
;
577 params
.stream
= This
->stream
;
580 wine_unix_call(get_buffer_size
, ¶ms
);
582 return params
.result
;
585 static HRESULT WINAPI
client_GetStreamLatency(IAudioClient3
*iface
, REFERENCE_TIME
*latency
)
587 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
588 struct get_latency_params params
;
590 TRACE("(%p)->(%p)\n", This
, latency
);
596 return AUDCLNT_E_NOT_INITIALIZED
;
598 params
.stream
= This
->stream
;
599 params
.latency
= latency
;
601 wine_unix_call(get_latency
, ¶ms
);
603 return params
.result
;
606 static HRESULT WINAPI
client_GetCurrentPadding(IAudioClient3
*iface
, UINT32
*out
)
608 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
609 struct get_current_padding_params params
;
611 TRACE("(%p)->(%p)\n", This
, out
);
617 return AUDCLNT_E_NOT_INITIALIZED
;
619 params
.stream
= This
->stream
;
620 params
.padding
= out
;
622 wine_unix_call(get_current_padding
, ¶ms
);
624 return params
.result
;
627 static HRESULT WINAPI
client_IsFormatSupported(IAudioClient3
*iface
, AUDCLNT_SHAREMODE mode
,
628 const WAVEFORMATEX
*fmt
, WAVEFORMATEX
**out
)
630 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
631 struct is_format_supported_params params
;
633 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
638 params
.device
= This
->device_name
;
639 params
.flow
= This
->dataflow
;
642 params
.fmt_out
= NULL
;
646 if (mode
== AUDCLNT_SHAREMODE_SHARED
)
647 params
.fmt_out
= CoTaskMemAlloc(sizeof(*params
.fmt_out
));
650 wine_unix_call(is_format_supported
, ¶ms
);
652 if (params
.result
== S_FALSE
)
653 *out
= ¶ms
.fmt_out
->Format
;
655 CoTaskMemFree(params
.fmt_out
);
657 return params
.result
;
660 static HRESULT WINAPI
client_GetMixFormat(IAudioClient3
*iface
, WAVEFORMATEX
**pwfx
)
662 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
663 struct get_mix_format_params params
;
665 TRACE("(%p)->(%p)\n", This
, pwfx
);
672 params
.device
= This
->device_name
;
673 params
.flow
= This
->dataflow
;
674 params
.fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
676 return E_OUTOFMEMORY
;
678 wine_unix_call(get_mix_format
, ¶ms
);
680 if (SUCCEEDED(params
.result
)) {
681 *pwfx
= ¶ms
.fmt
->Format
;
684 CoTaskMemFree(params
.fmt
);
686 return params
.result
;
689 static HRESULT WINAPI
client_GetDevicePeriod(IAudioClient3
*iface
, REFERENCE_TIME
*defperiod
,
690 REFERENCE_TIME
*minperiod
)
692 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
693 struct get_device_period_params params
;
695 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
697 if (!defperiod
&& !minperiod
)
700 params
.device
= This
->device_name
;
701 params
.flow
= This
->dataflow
;
702 params
.def_period
= defperiod
;
703 params
.min_period
= minperiod
;
705 wine_unix_call(get_device_period
, ¶ms
);
707 return params
.result
;
710 static HRESULT WINAPI
client_Start(IAudioClient3
*iface
)
712 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
713 struct start_params params
;
715 TRACE("(%p)\n", This
);
721 return AUDCLNT_E_NOT_INITIALIZED
;
724 params
.stream
= This
->stream
;
725 wine_unix_call(start
, ¶ms
);
727 if (SUCCEEDED(params
.result
) && !This
->timer_thread
) {
728 if ((This
->timer_thread
= CreateThread(NULL
, 0, timer_loop_func
, This
, 0, NULL
)))
729 SetThreadPriority(This
->timer_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
731 IAudioClient3_Stop(&This
->IAudioClient3_iface
);
732 params
.result
= E_FAIL
;
738 return params
.result
;
741 static HRESULT WINAPI
client_Stop(IAudioClient3
*iface
)
743 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
744 struct stop_params params
;
746 TRACE("(%p)\n", This
);
749 return AUDCLNT_E_NOT_INITIALIZED
;
751 params
.stream
= This
->stream
;
753 wine_unix_call(stop
, ¶ms
);
755 return params
.result
;
758 static HRESULT WINAPI
client_Reset(IAudioClient3
*iface
)
760 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
761 struct reset_params params
;
763 TRACE("(%p)\n", This
);
766 return AUDCLNT_E_NOT_INITIALIZED
;
768 params
.stream
= This
->stream
;
770 wine_unix_call(reset
, ¶ms
);
772 return params
.result
;
775 static HRESULT WINAPI
client_SetEventHandle(IAudioClient3
*iface
, HANDLE event
)
777 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
778 struct set_event_handle_params params
;
780 TRACE("(%p)->(%p)\n", This
, event
);
786 return AUDCLNT_E_NOT_INITIALIZED
;
788 params
.stream
= This
->stream
;
789 params
.event
= event
;
791 wine_unix_call(set_event_handle
, ¶ms
);
793 return params
.result
;
796 static HRESULT WINAPI
client_GetService(IAudioClient3
*iface
, REFIID riid
, void **ppv
)
798 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
801 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
811 hr
= AUDCLNT_E_NOT_INITIALIZED
;
815 if (IsEqualIID(riid
, &IID_IAudioRenderClient
)) {
816 if (This
->dataflow
!= eRender
) {
817 hr
= AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
821 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
822 *ppv
= &This
->IAudioRenderClient_iface
;
823 } else if (IsEqualIID(riid
, &IID_IAudioCaptureClient
)) {
824 if (This
->dataflow
!= eCapture
) {
825 hr
= AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
829 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
830 *ppv
= &This
->IAudioCaptureClient_iface
;
831 } else if (IsEqualIID(riid
, &IID_IAudioClock
)) {
832 IAudioClock_AddRef(&This
->IAudioClock_iface
);
833 *ppv
= &This
->IAudioClock_iface
;
834 } else if (IsEqualIID(riid
, &IID_IAudioStreamVolume
)) {
835 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
836 *ppv
= &This
->IAudioStreamVolume_iface
;
837 } else if (IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
838 IsEqualIID(riid
, &IID_IChannelAudioVolume
) ||
839 IsEqualIID(riid
, &IID_ISimpleAudioVolume
)) {
840 const BOOLEAN new_session
= !This
->session_wrapper
;
842 This
->session_wrapper
= session_wrapper_create(This
);
843 if (!This
->session_wrapper
) {
849 if (IsEqualIID(riid
, &IID_IAudioSessionControl
))
850 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
851 else if (IsEqualIID(riid
, &IID_IChannelAudioVolume
))
852 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
853 else if (IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
854 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
857 IUnknown_AddRef((IUnknown
*)*ppv
);
859 FIXME("stub %s\n", debugstr_guid(riid
));
871 static HRESULT WINAPI
client_IsOffloadCapable(IAudioClient3
*iface
, AUDIO_STREAM_CATEGORY category
,
872 BOOL
*offload_capable
)
874 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
876 TRACE("(%p)->(0x%x, %p)\n", This
, category
, offload_capable
);
878 if (!offload_capable
)
881 *offload_capable
= FALSE
;
886 static HRESULT WINAPI
client_SetClientProperties(IAudioClient3
*iface
,
887 const AudioClientProperties
*prop
)
889 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
890 const Win8AudioClientProperties
*legacy_prop
= (const Win8AudioClientProperties
*)prop
;
892 TRACE("(%p)->(%p)\n", This
, prop
);
897 if (legacy_prop
->cbSize
== sizeof(AudioClientProperties
)) {
898 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n", legacy_prop
->bIsOffload
,
899 legacy_prop
->eCategory
,
901 } else if(legacy_prop
->cbSize
== sizeof(Win8AudioClientProperties
)) {
902 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n", legacy_prop
->bIsOffload
,
903 legacy_prop
->eCategory
);
905 WARN("Unsupported Size = %d\n", legacy_prop
->cbSize
);
909 if (legacy_prop
->bIsOffload
)
910 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE
;
915 static HRESULT WINAPI
client_GetBufferSizeLimits(IAudioClient3
*iface
, const WAVEFORMATEX
*format
,
916 BOOL event_driven
, REFERENCE_TIME
*min_duration
,
917 REFERENCE_TIME
*max_duration
)
919 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
920 FIXME("(%p)->(%p, %u, %p, %p) - stub\n", This
, format
, event_driven
, min_duration
, max_duration
);
924 static HRESULT WINAPI
client_GetSharedModeEnginePeriod(IAudioClient3
*iface
,
925 const WAVEFORMATEX
*format
,
926 UINT32
*default_period_frames
,
927 UINT32
*unit_period_frames
,
928 UINT32
*min_period_frames
,
929 UINT32
*max_period_frames
)
931 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
932 FIXME("(%p)->(%p, %p, %p, %p, %p) - stub\n", This
, format
, default_period_frames
,
933 unit_period_frames
, min_period_frames
,
938 static HRESULT WINAPI
client_GetCurrentSharedModeEnginePeriod(IAudioClient3
*iface
,
939 WAVEFORMATEX
**cur_format
,
940 UINT32
*cur_period_frames
)
942 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
943 FIXME("(%p)->(%p, %p) - stub\n", This
, cur_format
, cur_period_frames
);
947 static HRESULT WINAPI
client_InitializeSharedAudioStream(IAudioClient3
*iface
, DWORD flags
,
948 UINT32 period_frames
,
949 const WAVEFORMATEX
*format
,
950 const GUID
*session_guid
)
952 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
953 FIXME("(%p)->(0x%lx, %u, %p, %s) - stub\n", This
, flags
, period_frames
, format
, debugstr_guid(session_guid
));
957 const IAudioClient3Vtbl AudioClient3_Vtbl
=
959 client_QueryInterface
,
963 client_GetBufferSize
,
964 client_GetStreamLatency
,
965 client_GetCurrentPadding
,
966 client_IsFormatSupported
,
968 client_GetDevicePeriod
,
972 client_SetEventHandle
,
974 client_IsOffloadCapable
,
975 client_SetClientProperties
,
976 client_GetBufferSizeLimits
,
977 client_GetSharedModeEnginePeriod
,
978 client_GetCurrentSharedModeEnginePeriod
,
979 client_InitializeSharedAudioStream
,
982 static HRESULT WINAPI
clock_QueryInterface(IAudioClock
*iface
, REFIID riid
, void **ppv
)
984 struct audio_client
*This
= impl_from_IAudioClock(iface
);
986 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
991 if (IsEqualIID(riid
, &IID_IUnknown
) ||
992 IsEqualIID(riid
, &IID_IAudioClock
))
994 else if (IsEqualIID(riid
, &IID_IAudioClock2
))
995 *ppv
= &This
->IAudioClock2_iface
;
996 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
997 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1000 return E_NOINTERFACE
;
1003 IUnknown_AddRef((IUnknown
*)*ppv
);
1008 static ULONG WINAPI
clock_AddRef(IAudioClock
*iface
)
1010 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1011 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1014 static ULONG WINAPI
clock_Release(IAudioClock
*iface
)
1016 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1017 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1020 static HRESULT WINAPI
clock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
1022 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1023 struct get_frequency_params params
;
1025 TRACE("(%p)->(%p)\n", This
, freq
);
1028 return AUDCLNT_E_NOT_INITIALIZED
;
1030 params
.stream
= This
->stream
;
1033 wine_unix_call(get_frequency
, ¶ms
);
1035 return params
.result
;
1038 static HRESULT WINAPI
clock_GetPosition(IAudioClock
*iface
, UINT64
*pos
, UINT64
*qpctime
)
1040 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1041 struct get_position_params params
;
1043 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
1049 return AUDCLNT_E_NOT_INITIALIZED
;
1051 params
.stream
= This
->stream
;
1052 params
.device
= FALSE
;
1054 params
.qpctime
= qpctime
;
1056 wine_unix_call(get_position
, ¶ms
);
1058 return params
.result
;
1061 static HRESULT WINAPI
clock_GetCharacteristics(IAudioClock
*iface
, DWORD
*chars
)
1063 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1065 TRACE("(%p)->(%p)\n", This
, chars
);
1070 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
1075 const IAudioClockVtbl AudioClock_Vtbl
=
1077 clock_QueryInterface
,
1082 clock_GetCharacteristics
1085 static HRESULT WINAPI
clock2_QueryInterface(IAudioClock2
*iface
, REFIID riid
, void **ppv
)
1087 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1088 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
1091 static ULONG WINAPI
clock2_AddRef(IAudioClock2
*iface
)
1093 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1094 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1097 static ULONG WINAPI
clock2_Release(IAudioClock2
*iface
)
1099 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1100 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1103 static HRESULT WINAPI
clock2_GetDevicePosition(IAudioClock2
*iface
, UINT64
*pos
, UINT64
*qpctime
)
1105 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1106 struct get_position_params params
;
1108 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
1114 return AUDCLNT_E_NOT_INITIALIZED
;
1116 params
.stream
= This
->stream
;
1117 params
.device
= TRUE
;
1119 params
.qpctime
= qpctime
;
1121 wine_unix_call(get_position
, ¶ms
);
1123 return params
.result
;
1126 const IAudioClock2Vtbl AudioClock2_Vtbl
=
1128 clock2_QueryInterface
,
1131 clock2_GetDevicePosition
1134 static HRESULT WINAPI
render_QueryInterface(IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1136 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1138 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1143 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1144 IsEqualIID(riid
, &IID_IAudioRenderClient
))
1146 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
1147 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1150 return E_NOINTERFACE
;
1153 IUnknown_AddRef((IUnknown
*)*ppv
);
1158 static ULONG WINAPI
render_AddRef(IAudioRenderClient
*iface
)
1160 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1161 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1164 static ULONG WINAPI
render_Release(IAudioRenderClient
*iface
)
1166 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1167 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1170 static HRESULT WINAPI
render_GetBuffer(IAudioRenderClient
*iface
, UINT32 frames
, BYTE
**data
)
1172 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1173 struct get_render_buffer_params params
;
1175 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
1181 return AUDCLNT_E_NOT_INITIALIZED
;
1185 params
.stream
= This
->stream
;
1186 params
.frames
= frames
;
1189 wine_unix_call(get_render_buffer
, ¶ms
);
1191 return params
.result
;
1194 static HRESULT WINAPI
render_ReleaseBuffer(IAudioRenderClient
*iface
, UINT32 written_frames
,
1197 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1198 struct release_render_buffer_params params
;
1200 TRACE("(%p)->(%u, %lx)\n", This
, written_frames
, flags
);
1203 return AUDCLNT_E_NOT_INITIALIZED
;
1205 params
.stream
= This
->stream
;
1206 params
.written_frames
= written_frames
;
1207 params
.flags
= flags
;
1209 wine_unix_call(release_render_buffer
, ¶ms
);
1211 return params
.result
;
1214 const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
1215 render_QueryInterface
,
1219 render_ReleaseBuffer
1222 static HRESULT WINAPI
streamvolume_QueryInterface(IAudioStreamVolume
*iface
, REFIID riid
,
1225 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1230 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1231 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
1233 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
1234 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1235 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1238 return E_NOINTERFACE
;
1241 IUnknown_AddRef((IUnknown
*)*ppv
);
1246 static ULONG WINAPI
streamvolume_AddRef(IAudioStreamVolume
*iface
)
1248 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1249 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1252 static ULONG WINAPI
streamvolume_Release(IAudioStreamVolume
*iface
)
1254 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1255 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1258 static HRESULT WINAPI
streamvolume_GetChannelCount(IAudioStreamVolume
*iface
, UINT32
*out
)
1260 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1262 TRACE("(%p)->(%p)\n", This
, out
);
1267 *out
= This
->channel_count
;
1272 static HRESULT WINAPI
streamvolume_SetChannelVolume(IAudioStreamVolume
*iface
, UINT32 index
,
1275 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1277 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
1279 if (level
< 0.f
|| level
> 1.f
)
1280 return E_INVALIDARG
;
1283 return AUDCLNT_E_NOT_INITIALIZED
;
1285 if (index
>= This
->channel_count
)
1286 return E_INVALIDARG
;
1290 This
->vols
[index
] = level
;
1291 set_stream_volumes(This
);
1298 static HRESULT WINAPI
streamvolume_GetChannelVolume(IAudioStreamVolume
*iface
, UINT32 index
,
1301 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1303 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
1309 return AUDCLNT_E_NOT_INITIALIZED
;
1311 if (index
>= This
->channel_count
)
1312 return E_INVALIDARG
;
1314 *level
= This
->vols
[index
];
1319 static HRESULT WINAPI
streamvolume_SetAllVolumes(IAudioStreamVolume
*iface
, UINT32 count
,
1320 const float *levels
)
1322 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1325 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
1331 return AUDCLNT_E_NOT_INITIALIZED
;
1333 if (count
!= This
->channel_count
)
1334 return E_INVALIDARG
;
1338 for (i
= 0; i
< count
; ++i
)
1339 This
->vols
[i
] = levels
[i
];
1340 set_stream_volumes(This
);
1347 static HRESULT WINAPI
streamvolume_GetAllVolumes(IAudioStreamVolume
*iface
, UINT32 count
,
1350 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1353 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
1359 return AUDCLNT_E_NOT_INITIALIZED
;
1361 if (count
!= This
->channel_count
)
1362 return E_INVALIDARG
;
1366 for (i
= 0; i
< count
; ++i
)
1367 levels
[i
] = This
->vols
[i
];
1374 const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
1376 streamvolume_QueryInterface
,
1377 streamvolume_AddRef
,
1378 streamvolume_Release
,
1379 streamvolume_GetChannelCount
,
1380 streamvolume_SetChannelVolume
,
1381 streamvolume_GetChannelVolume
,
1382 streamvolume_SetAllVolumes
,
1383 streamvolume_GetAllVolumes
1386 HRESULT
AudioClient_Create(GUID
*guid
, IMMDevice
*device
, IAudioClient
**out
)
1388 struct audio_client
*This
;
1394 TRACE("%s %p %p\n", debugstr_guid(guid
), device
, out
);
1398 if (!drvs
.pget_device_name_from_guid(guid
, &name
, &dataflow
))
1399 return AUDCLNT_E_DEVICE_INVALIDATED
;
1401 if (dataflow
!= eRender
&& dataflow
!= eCapture
) {
1403 return E_UNEXPECTED
;
1406 size
= strlen(name
) + 1;
1407 This
= calloc(1, FIELD_OFFSET(struct audio_client
, device_name
[size
]));
1410 return E_OUTOFMEMORY
;
1413 memcpy(This
->device_name
, name
, size
);
1416 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
1417 This
->IAudioClient3_iface
.lpVtbl
= &AudioClient3_Vtbl
;
1418 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
1419 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
1420 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
1421 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
1423 This
->dataflow
= dataflow
;
1424 This
->parent
= device
;
1426 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient3_iface
, &This
->marshal
);
1432 IMMDevice_AddRef(This
->parent
);
1434 *out
= (IAudioClient
*)&This
->IAudioClient3_iface
;
1435 IAudioClient3_AddRef(&This
->IAudioClient3_iface
);