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
25 #define WIN32_NO_STATUS
29 #include <audiopolicy.h>
30 #include <mmdeviceapi.h>
33 #include <wine/debug.h>
34 #include <wine/unixlib.h>
36 #include "mmdevapi_private.h"
38 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
40 typedef struct tagLANGANDCODEPAGE
46 extern void sessions_lock(void);
47 extern void sessions_unlock(void);
49 extern HRESULT
get_audio_session(const GUID
*sessionguid
, IMMDevice
*device
, UINT channels
,
50 struct audio_session
**out
);
51 extern struct audio_session_wrapper
*session_wrapper_create(struct audio_client
*client
);
53 static HANDLE main_loop_thread
;
55 void main_loop_stop(void)
57 if (main_loop_thread
) {
58 WaitForSingleObject(main_loop_thread
, INFINITE
);
59 CloseHandle(main_loop_thread
);
63 void set_stream_volumes(struct audio_client
*This
)
65 struct set_volumes_params params
;
67 params
.stream
= This
->stream
;
68 params
.master_volume
= (This
->session
->mute
? 0.0f
: This
->session
->master_vol
);
69 params
.volumes
= This
->vols
;
70 params
.session_volumes
= This
->session
->channel_vols
;
72 wine_unix_call(set_volumes
, ¶ms
);
75 static inline struct audio_client
*impl_from_IAudioCaptureClient(IAudioCaptureClient
*iface
)
77 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioCaptureClient_iface
);
80 static inline struct audio_client
*impl_from_IAudioClient3(IAudioClient3
*iface
)
82 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioClient3_iface
);
85 static inline struct audio_client
*impl_from_IAudioClock(IAudioClock
*iface
)
87 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioClock_iface
);
90 static inline struct audio_client
*impl_from_IAudioClock2(IAudioClock2
*iface
)
92 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioClock2_iface
);
95 static inline struct audio_client
*impl_from_IAudioRenderClient(IAudioRenderClient
*iface
)
97 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioRenderClient_iface
);
100 static inline struct audio_client
*impl_from_IAudioStreamVolume(IAudioStreamVolume
*iface
)
102 return CONTAINING_RECORD(iface
, struct audio_client
, IAudioStreamVolume_iface
);
105 static HRESULT
get_periods(struct audio_client
*client
,
106 REFERENCE_TIME
*def_period
, REFERENCE_TIME
*min_period
)
108 static const REFERENCE_TIME min_def_period
= 100000; /* 10 ms */
109 struct get_device_period_params params
;
111 params
.device
= client
->device_name
;
112 params
.flow
= client
->dataflow
;
113 params
.def_period
= def_period
;
114 params
.min_period
= min_period
;
116 wine_unix_call(get_device_period
, ¶ms
);
118 if (def_period
) *def_period
= max(*def_period
, min_def_period
);
120 return params
.result
;
123 static HRESULT
adjust_timing(struct audio_client
*client
, const BOOLEAN force_def_period
,
124 REFERENCE_TIME
*duration
, REFERENCE_TIME
*period
,
125 const AUDCLNT_SHAREMODE mode
, const DWORD flags
,
126 const WAVEFORMATEX
*fmt
)
128 REFERENCE_TIME def_period
, min_period
;
131 TRACE("Requested duration %lu and period %lu\n", (ULONG
)*duration
, (ULONG
)*period
);
133 if (FAILED(hr
= get_periods(client
, &def_period
, &min_period
)))
136 TRACE("Device periods: %lu default and %lu minimum\n", (ULONG
)def_period
, (ULONG
)min_period
);
138 if (mode
== AUDCLNT_SHAREMODE_SHARED
) {
139 if (*period
== 0 || force_def_period
)
140 *period
= def_period
;
141 else if (*period
< min_period
)
142 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
143 if (*duration
< 3 * *period
)
144 *duration
= 3 * *period
;
146 const WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)fmt
;
147 if (fmtex
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
148 (fmtex
->dwChannelMask
== 0 || fmtex
->dwChannelMask
& SPEAKER_RESERVED
))
149 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
152 *period
= def_period
;
153 if (*period
< min_period
|| *period
> 5000000)
154 return AUDCLNT_E_INVALID_DEVICE_PERIOD
;
155 else if (*duration
> 20000000) /* The smaller the period, the lower this limit. */
156 return AUDCLNT_E_BUFFER_SIZE_ERROR
;
157 else if (flags
& AUDCLNT_STREAMFLAGS_EVENTCALLBACK
) {
158 if (*duration
!= *period
)
159 return AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL
;
161 FIXME("EXCLUSIVE mode with EVENTCALLBACK\n");
163 return AUDCLNT_E_DEVICE_IN_USE
;
164 } else if (*duration
< 8 * *period
)
165 *duration
= 8 * *period
; /* May grow above 2s. */
169 TRACE("Adjusted duration %lu and period %lu\n", (ULONG
)*duration
, (ULONG
)*period
);
174 static void dump_fmt(const WAVEFORMATEX
*fmt
)
176 TRACE("wFormatTag: 0x%x (", fmt
->wFormatTag
);
177 switch (fmt
->wFormatTag
) {
178 case WAVE_FORMAT_PCM
:
179 TRACE("WAVE_FORMAT_PCM");
181 case WAVE_FORMAT_IEEE_FLOAT
:
182 TRACE("WAVE_FORMAT_IEEE_FLOAT");
184 case WAVE_FORMAT_EXTENSIBLE
:
185 TRACE("WAVE_FORMAT_EXTENSIBLE");
193 TRACE("nChannels: %u\n", fmt
->nChannels
);
194 TRACE("nSamplesPerSec: %lu\n", fmt
->nSamplesPerSec
);
195 TRACE("nAvgBytesPerSec: %lu\n", fmt
->nAvgBytesPerSec
);
196 TRACE("nBlockAlign: %u\n", fmt
->nBlockAlign
);
197 TRACE("wBitsPerSample: %u\n", fmt
->wBitsPerSample
);
198 TRACE("cbSize: %u\n", fmt
->cbSize
);
200 if (fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
) {
201 WAVEFORMATEXTENSIBLE
*fmtex
= (void *)fmt
;
202 TRACE("dwChannelMask: %08lx\n", fmtex
->dwChannelMask
);
203 TRACE("Samples: %04x\n", fmtex
->Samples
.wReserved
);
204 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex
->SubFormat
));
208 static DWORD CALLBACK
main_loop_func(void *event
)
210 struct main_loop_params params
;
212 SetThreadDescription(GetCurrentThread(), L
"audio_client_main");
214 params
.event
= event
;
216 wine_unix_call(main_loop
, ¶ms
);
221 HRESULT
main_loop_start(void)
223 if (!main_loop_thread
) {
224 HANDLE event
= CreateEventW(NULL
, TRUE
, FALSE
, NULL
);
225 if (!(main_loop_thread
= CreateThread(NULL
, 0, main_loop_func
, event
, 0, NULL
))) {
226 ERR("Failed to create main loop thread\n");
231 SetThreadPriority(main_loop_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
232 WaitForSingleObject(event
, INFINITE
);
239 static DWORD CALLBACK
timer_loop_func(void *user
)
241 struct timer_loop_params params
;
242 struct audio_client
*This
= user
;
244 SetThreadDescription(GetCurrentThread(), L
"audio_client_timer");
246 params
.stream
= This
->stream
;
248 wine_unix_call(timer_loop
, ¶ms
);
253 HRESULT
stream_release(stream_handle stream
, HANDLE timer_thread
)
255 struct release_stream_params params
;
257 params
.stream
= stream
;
258 params
.timer_thread
= timer_thread
;
260 wine_unix_call(release_stream
, ¶ms
);
262 return params
.result
;
265 static BOOL
query_productname(void *data
, LANGANDCODEPAGE
*lang
, LPVOID
*buffer
, UINT
*len
)
268 swprintf(pn
, ARRAY_SIZE(pn
), L
"\\StringFileInfo\\%04x%04x\\ProductName", lang
->wLanguage
, lang
->wCodePage
);
269 return VerQueryValueW(data
, pn
, buffer
, len
) && *len
;
272 WCHAR
*get_application_name(void)
274 WCHAR path
[MAX_PATH
], *name
;
275 UINT translate_size
, productname_size
;
276 LANGANDCODEPAGE
*translate
;
284 GetModuleFileNameW(NULL
, path
, ARRAY_SIZE(path
));
286 size
= GetFileVersionInfoSizeW(path
, NULL
);
294 if (!GetFileVersionInfoW(path
, 0, size
, data
))
297 if (!VerQueryValueW(data
, L
"\\VarFileInfo\\Translation", (LPVOID
*)&translate
, &translate_size
))
300 /* No translations found. */
301 if (translate_size
< sizeof(LANGANDCODEPAGE
))
304 /* The following code will try to find the best translation. We first search for an
305 * exact match of the language, then a match of the language PRIMARYLANGID, then we
306 * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the
307 * first entry which contains a proper productname. */
308 locale
= GetThreadLocale();
310 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
311 if (translate
[i
].wLanguage
== locale
&&
312 query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
319 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
320 if (PRIMARYLANGID(translate
[i
].wLanguage
) == PRIMARYLANGID(locale
) &&
321 query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
329 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
330 if (PRIMARYLANGID(translate
[i
].wLanguage
) == LANG_NEUTRAL
&&
331 query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
339 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
340 if (query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
348 name
= wcsdup(productname
);
355 name
= wcsrchr(path
, '\\');
364 static HRESULT
stream_init(struct audio_client
*client
, const BOOLEAN force_def_period
,
365 const AUDCLNT_SHAREMODE mode
, const DWORD flags
,
366 REFERENCE_TIME duration
, REFERENCE_TIME period
,
367 const WAVEFORMATEX
*fmt
, const GUID
*sessionguid
)
369 struct create_stream_params params
;
370 UINT32 i
, channel_count
;
371 stream_handle stream
;
379 if (mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
382 if (flags
& ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS
|
383 AUDCLNT_STREAMFLAGS_LOOPBACK
|
384 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
|
385 AUDCLNT_STREAMFLAGS_NOPERSIST
|
386 AUDCLNT_STREAMFLAGS_RATEADJUST
|
387 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED
|
388 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE
|
389 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED
|
390 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY
|
391 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM
)) {
392 FIXME("Unknown flags: %08lx\n", flags
);
398 if (client
->stream
) {
400 return AUDCLNT_E_ALREADY_INITIALIZED
;
403 if (FAILED(params
.result
= main_loop_start())) {
405 return params
.result
;
408 if (flags
& AUDCLNT_STREAMFLAGS_LOOPBACK
) {
409 struct get_loopback_capture_device_params params
;
411 if (client
->dataflow
!= eRender
) {
413 return AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
416 params
.device
= client
->device_name
;
417 params
.name
= name
= get_application_name();
418 params
.ret_device_len
= 0;
419 params
.ret_device
= NULL
;
420 params
.result
= E_NOTIMPL
;
421 wine_unix_call(get_loopback_capture_device
, ¶ms
);
422 while (params
.result
== STATUS_BUFFER_TOO_SMALL
) {
423 free(params
.ret_device
);
424 params
.ret_device
= malloc(params
.ret_device_len
);
425 wine_unix_call(get_loopback_capture_device
, ¶ms
);
428 if (FAILED(params
.result
)) {
430 free(params
.ret_device
);
431 if (params
.result
== E_NOTIMPL
)
432 FIXME("get_loopback_capture_device is not supported by backend.\n");
433 return params
.result
;
435 free(client
->device_name
);
436 client
->device_name
= params
.ret_device
;
437 client
->dataflow
= eCapture
;
440 if (FAILED(params
.result
= adjust_timing(client
, force_def_period
, &duration
, &period
, mode
, flags
, fmt
))) {
442 return params
.result
;
445 params
.name
= name
= get_application_name();
446 params
.device
= client
->device_name
;
447 params
.flow
= client
->dataflow
;
449 params
.flags
= flags
;
450 params
.duration
= duration
;
451 params
.period
= period
;
453 params
.channel_count
= &channel_count
;
454 params
.stream
= &stream
;
456 wine_unix_call(create_stream
, ¶ms
);
460 if (FAILED(params
.result
)) {
462 return params
.result
;
465 if (!(client
->vols
= malloc(channel_count
* sizeof(*client
->vols
)))) {
466 params
.result
= E_OUTOFMEMORY
;
470 for (i
= 0; i
< channel_count
; i
++)
471 client
->vols
[i
] = 1.f
;
473 params
.result
= get_audio_session(sessionguid
, client
->parent
, channel_count
, &client
->session
);
476 if (FAILED(params
.result
)) {
477 stream_release(stream
, NULL
);
481 list_add_tail(&client
->session
->clients
, &client
->entry
);
482 client
->stream
= stream
;
483 client
->channel_count
= channel_count
;
484 set_stream_volumes(client
);
489 return params
.result
;
492 static HRESULT WINAPI
capture_QueryInterface(IAudioCaptureClient
*iface
, REFIID riid
, void **ppv
)
494 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
496 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
501 if (IsEqualIID(riid
, &IID_IUnknown
) ||
502 IsEqualIID(riid
, &IID_IAudioCaptureClient
))
504 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
505 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
508 return E_NOINTERFACE
;
511 IUnknown_AddRef((IUnknown
*)*ppv
);
516 static ULONG WINAPI
capture_AddRef(IAudioCaptureClient
*iface
)
518 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
519 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
522 static ULONG WINAPI
capture_Release(IAudioCaptureClient
*iface
)
524 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
525 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
528 static HRESULT WINAPI
capture_GetBuffer(IAudioCaptureClient
*iface
, BYTE
**data
, UINT32
*frames
,
529 DWORD
*flags
, UINT64
*devpos
, UINT64
*qpcpos
)
531 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
532 struct get_capture_buffer_params params
;
534 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This
, data
, frames
, flags
, devpos
, qpcpos
);
541 if (!frames
|| !flags
)
545 return AUDCLNT_E_NOT_INITIALIZED
;
547 params
.stream
= This
->stream
;
549 params
.frames
= frames
;
550 params
.flags
= (UINT
*)flags
;
551 params
.devpos
= devpos
;
552 params
.qpcpos
= qpcpos
;
554 wine_unix_call(get_capture_buffer
, ¶ms
);
556 return params
.result
;
559 static HRESULT WINAPI
capture_ReleaseBuffer(IAudioCaptureClient
*iface
, UINT32 done
)
561 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
562 struct release_capture_buffer_params params
;
564 TRACE("(%p)->(%u)\n", This
, done
);
567 return AUDCLNT_E_NOT_INITIALIZED
;
569 params
.stream
= This
->stream
;
572 wine_unix_call(release_capture_buffer
, ¶ms
);
574 return params
.result
;
577 static HRESULT WINAPI
capture_GetNextPacketSize(IAudioCaptureClient
*iface
, UINT32
*frames
)
579 struct audio_client
*This
= impl_from_IAudioCaptureClient(iface
);
580 struct get_next_packet_size_params params
;
582 TRACE("(%p)->(%p)\n", This
, frames
);
588 return AUDCLNT_E_NOT_INITIALIZED
;
590 params
.stream
= This
->stream
;
591 params
.frames
= frames
;
593 wine_unix_call(get_next_packet_size
, ¶ms
);
595 return params
.result
;
598 const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl
=
600 capture_QueryInterface
,
604 capture_ReleaseBuffer
,
605 capture_GetNextPacketSize
608 static HRESULT WINAPI
client_QueryInterface(IAudioClient3
*iface
, REFIID riid
, void **ppv
)
610 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
615 if (IsEqualIID(riid
, &IID_IUnknown
) ||
616 IsEqualIID(riid
, &IID_IAudioClient
) ||
617 IsEqualIID(riid
, &IID_IAudioClient2
) ||
618 IsEqualIID(riid
, &IID_IAudioClient3
))
620 else if(IsEqualIID(riid
, &IID_IMarshal
)) {
621 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
622 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
625 return E_NOINTERFACE
;
628 IUnknown_AddRef((IUnknown
*)*ppv
);
633 static ULONG WINAPI
client_AddRef(IAudioClient3
*iface
)
635 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
636 ULONG ref
= InterlockedIncrement(&This
->ref
);
637 TRACE("(%p) Refcount now %lu\n", This
, ref
);
641 static ULONG WINAPI
client_Release(IAudioClient3
*iface
)
643 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
644 ULONG ref
= InterlockedDecrement(&This
->ref
);
645 TRACE("(%p) Refcount now %lu\n", This
, ref
);
648 IAudioClient3_Stop(iface
);
649 IMMDevice_Release(This
->parent
);
650 IUnknown_Release(This
->marshal
);
654 list_remove(&This
->entry
);
661 stream_release(This
->stream
, This
->timer_thread
);
663 free(This
->device_name
);
670 static HRESULT WINAPI
client_Initialize(IAudioClient3
*iface
, AUDCLNT_SHAREMODE mode
, DWORD flags
,
671 REFERENCE_TIME duration
, REFERENCE_TIME period
,
672 const WAVEFORMATEX
*fmt
, const GUID
*sessionguid
)
674 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
676 TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This
, mode
, flags
, wine_dbgstr_longlong(duration
),
677 wine_dbgstr_longlong(period
), fmt
,
678 debugstr_guid(sessionguid
));
680 return stream_init(This
, TRUE
, mode
, flags
, duration
, period
, fmt
, sessionguid
);
683 static HRESULT WINAPI
client_GetBufferSize(IAudioClient3
*iface
, UINT32
*out
)
685 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
686 struct get_buffer_size_params params
;
688 TRACE("(%p)->(%p)\n", This
, out
);
694 return AUDCLNT_E_NOT_INITIALIZED
;
696 params
.stream
= This
->stream
;
699 wine_unix_call(get_buffer_size
, ¶ms
);
701 return params
.result
;
704 static HRESULT WINAPI
client_GetStreamLatency(IAudioClient3
*iface
, REFERENCE_TIME
*latency
)
706 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
707 struct get_latency_params params
;
709 TRACE("(%p)->(%p)\n", This
, latency
);
715 return AUDCLNT_E_NOT_INITIALIZED
;
717 params
.stream
= This
->stream
;
718 params
.latency
= latency
;
720 wine_unix_call(get_latency
, ¶ms
);
722 return params
.result
;
725 static HRESULT WINAPI
client_GetCurrentPadding(IAudioClient3
*iface
, UINT32
*out
)
727 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
728 struct get_current_padding_params params
;
730 TRACE("(%p)->(%p)\n", This
, out
);
736 return AUDCLNT_E_NOT_INITIALIZED
;
738 params
.stream
= This
->stream
;
739 params
.padding
= out
;
741 wine_unix_call(get_current_padding
, ¶ms
);
743 return params
.result
;
746 static HRESULT WINAPI
client_IsFormatSupported(IAudioClient3
*iface
, AUDCLNT_SHAREMODE mode
,
747 const WAVEFORMATEX
*fmt
, WAVEFORMATEX
**out
)
749 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
750 struct is_format_supported_params params
;
752 TRACE("(%p)->(%x, %p, %p)\n", This
, mode
, fmt
, out
);
757 params
.device
= This
->device_name
;
758 params
.flow
= This
->dataflow
;
761 params
.fmt_out
= NULL
;
765 if (mode
== AUDCLNT_SHAREMODE_SHARED
)
766 params
.fmt_out
= CoTaskMemAlloc(sizeof(*params
.fmt_out
));
769 wine_unix_call(is_format_supported
, ¶ms
);
771 if (params
.result
== S_FALSE
)
772 *out
= ¶ms
.fmt_out
->Format
;
774 CoTaskMemFree(params
.fmt_out
);
776 return params
.result
;
779 static HRESULT WINAPI
client_GetMixFormat(IAudioClient3
*iface
, WAVEFORMATEX
**pwfx
)
781 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
782 struct get_mix_format_params params
;
784 TRACE("(%p)->(%p)\n", This
, pwfx
);
791 params
.device
= This
->device_name
;
792 params
.flow
= This
->dataflow
;
793 params
.fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
795 return E_OUTOFMEMORY
;
797 wine_unix_call(get_mix_format
, ¶ms
);
799 if (SUCCEEDED(params
.result
)) {
800 *pwfx
= ¶ms
.fmt
->Format
;
803 CoTaskMemFree(params
.fmt
);
805 return params
.result
;
808 static HRESULT WINAPI
client_GetDevicePeriod(IAudioClient3
*iface
, REFERENCE_TIME
*defperiod
,
809 REFERENCE_TIME
*minperiod
)
811 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
813 TRACE("(%p)->(%p, %p)\n", This
, defperiod
, minperiod
);
815 if (!defperiod
&& !minperiod
)
818 return get_periods(This
, defperiod
, minperiod
);
821 static HRESULT WINAPI
client_Start(IAudioClient3
*iface
)
823 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
824 struct start_params params
;
826 TRACE("(%p)\n", This
);
832 return AUDCLNT_E_NOT_INITIALIZED
;
835 params
.stream
= This
->stream
;
836 wine_unix_call(start
, ¶ms
);
838 if (SUCCEEDED(params
.result
) && !This
->timer_thread
) {
839 if ((This
->timer_thread
= CreateThread(NULL
, 0, timer_loop_func
, This
, 0, NULL
)))
840 SetThreadPriority(This
->timer_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
842 IAudioClient3_Stop(&This
->IAudioClient3_iface
);
843 params
.result
= E_FAIL
;
849 return params
.result
;
852 static HRESULT WINAPI
client_Stop(IAudioClient3
*iface
)
854 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
855 struct stop_params params
;
857 TRACE("(%p)\n", This
);
860 return AUDCLNT_E_NOT_INITIALIZED
;
862 params
.stream
= This
->stream
;
864 wine_unix_call(stop
, ¶ms
);
866 return params
.result
;
869 static HRESULT WINAPI
client_Reset(IAudioClient3
*iface
)
871 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
872 struct reset_params params
;
874 TRACE("(%p)\n", This
);
877 return AUDCLNT_E_NOT_INITIALIZED
;
879 params
.stream
= This
->stream
;
881 wine_unix_call(reset
, ¶ms
);
883 return params
.result
;
886 static HRESULT WINAPI
client_SetEventHandle(IAudioClient3
*iface
, HANDLE event
)
888 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
889 struct set_event_handle_params params
;
891 TRACE("(%p)->(%p)\n", This
, event
);
897 return AUDCLNT_E_NOT_INITIALIZED
;
899 params
.stream
= This
->stream
;
900 params
.event
= event
;
902 wine_unix_call(set_event_handle
, ¶ms
);
904 return params
.result
;
907 static HRESULT WINAPI
client_GetService(IAudioClient3
*iface
, REFIID riid
, void **ppv
)
909 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
912 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
922 hr
= AUDCLNT_E_NOT_INITIALIZED
;
926 if (IsEqualIID(riid
, &IID_IAudioRenderClient
)) {
927 if (This
->dataflow
!= eRender
) {
928 hr
= AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
932 IAudioRenderClient_AddRef(&This
->IAudioRenderClient_iface
);
933 *ppv
= &This
->IAudioRenderClient_iface
;
934 } else if (IsEqualIID(riid
, &IID_IAudioCaptureClient
)) {
935 if (This
->dataflow
!= eCapture
) {
936 hr
= AUDCLNT_E_WRONG_ENDPOINT_TYPE
;
940 IAudioCaptureClient_AddRef(&This
->IAudioCaptureClient_iface
);
941 *ppv
= &This
->IAudioCaptureClient_iface
;
942 } else if (IsEqualIID(riid
, &IID_IAudioClock
)) {
943 IAudioClock_AddRef(&This
->IAudioClock_iface
);
944 *ppv
= &This
->IAudioClock_iface
;
945 } else if (IsEqualIID(riid
, &IID_IAudioStreamVolume
)) {
946 IAudioStreamVolume_AddRef(&This
->IAudioStreamVolume_iface
);
947 *ppv
= &This
->IAudioStreamVolume_iface
;
948 } else if (IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
949 IsEqualIID(riid
, &IID_IChannelAudioVolume
) ||
950 IsEqualIID(riid
, &IID_ISimpleAudioVolume
)) {
951 const BOOLEAN new_session
= !This
->session_wrapper
;
953 This
->session_wrapper
= session_wrapper_create(This
);
954 if (!This
->session_wrapper
) {
960 if (IsEqualIID(riid
, &IID_IAudioSessionControl
))
961 *ppv
= &This
->session_wrapper
->IAudioSessionControl2_iface
;
962 else if (IsEqualIID(riid
, &IID_IChannelAudioVolume
))
963 *ppv
= &This
->session_wrapper
->IChannelAudioVolume_iface
;
964 else if (IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
965 *ppv
= &This
->session_wrapper
->ISimpleAudioVolume_iface
;
968 IUnknown_AddRef((IUnknown
*)*ppv
);
970 FIXME("stub %s\n", debugstr_guid(riid
));
982 static HRESULT WINAPI
client_IsOffloadCapable(IAudioClient3
*iface
, AUDIO_STREAM_CATEGORY category
,
983 BOOL
*offload_capable
)
985 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
987 TRACE("(%p)->(0x%x, %p)\n", This
, category
, offload_capable
);
989 if (!offload_capable
)
992 *offload_capable
= FALSE
;
997 static HRESULT WINAPI
client_SetClientProperties(IAudioClient3
*iface
,
998 const AudioClientProperties
*prop
)
1000 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
1001 const Win8AudioClientProperties
*legacy_prop
= (const Win8AudioClientProperties
*)prop
;
1003 TRACE("(%p)->(%p)\n", This
, prop
);
1008 if (legacy_prop
->cbSize
== sizeof(AudioClientProperties
)) {
1009 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n", legacy_prop
->bIsOffload
,
1010 legacy_prop
->eCategory
,
1012 } else if(legacy_prop
->cbSize
== sizeof(Win8AudioClientProperties
)) {
1013 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n", legacy_prop
->bIsOffload
,
1014 legacy_prop
->eCategory
);
1016 WARN("Unsupported Size = %d\n", legacy_prop
->cbSize
);
1017 return E_INVALIDARG
;
1020 if (legacy_prop
->bIsOffload
)
1021 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE
;
1026 static HRESULT WINAPI
client_GetBufferSizeLimits(IAudioClient3
*iface
, const WAVEFORMATEX
*format
,
1027 BOOL event_driven
, REFERENCE_TIME
*min_duration
,
1028 REFERENCE_TIME
*max_duration
)
1030 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
1031 FIXME("(%p)->(%p, %u, %p, %p) - stub\n", This
, format
, event_driven
, min_duration
, max_duration
);
1035 static HRESULT WINAPI
client_GetSharedModeEnginePeriod(IAudioClient3
*iface
,
1036 const WAVEFORMATEX
*format
,
1037 UINT32
*default_period_frames
,
1038 UINT32
*unit_period_frames
,
1039 UINT32
*min_period_frames
,
1040 UINT32
*max_period_frames
)
1042 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
1043 REFERENCE_TIME def_period
, min_period
;
1046 TRACE("(%p)->(%p, %p, %p, %p, %p)\n",
1047 This
, format
, default_period_frames
,
1048 unit_period_frames
, min_period_frames
,
1051 if (FAILED(hr
= get_periods(This
, &def_period
, &min_period
)))
1054 *default_period_frames
= def_period
* format
->nSamplesPerSec
/ (REFERENCE_TIME
)10000000;
1055 *min_period_frames
= min_period
* format
->nSamplesPerSec
/ (REFERENCE_TIME
)10000000;
1056 *max_period_frames
= *default_period_frames
;
1057 *unit_period_frames
= 1;
1062 static HRESULT WINAPI
client_GetCurrentSharedModeEnginePeriod(IAudioClient3
*iface
,
1063 WAVEFORMATEX
**cur_format
,
1064 UINT32
*cur_period_frames
)
1066 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
1070 TRACE("(%p)->(%p, %p)\n", This
, cur_format
, cur_period_frames
);
1072 if (!cur_format
|| !cur_period_frames
)
1075 if (FAILED(hr
= client_GetMixFormat(iface
, cur_format
)))
1078 return client_GetSharedModeEnginePeriod(iface
, *cur_format
, cur_period_frames
, &dummy
, &dummy
, &dummy
);
1081 static HRESULT WINAPI
client_InitializeSharedAudioStream(IAudioClient3
*iface
, DWORD flags
,
1082 UINT32 period_frames
,
1083 const WAVEFORMATEX
*format
,
1084 const GUID
*session_guid
)
1086 struct audio_client
*This
= impl_from_IAudioClient3(iface
);
1087 REFERENCE_TIME period
;
1089 TRACE("(%p)->(0x%lx, %u, %p, %s)\n", This
, flags
, period_frames
, format
, debugstr_guid(session_guid
));
1094 period
= period_frames
* (REFERENCE_TIME
)10000000 / format
->nSamplesPerSec
;
1096 return stream_init(This
, FALSE
, AUDCLNT_SHAREMODE_SHARED
, flags
, 0, period
, format
, session_guid
);
1099 const IAudioClient3Vtbl AudioClient3_Vtbl
=
1101 client_QueryInterface
,
1105 client_GetBufferSize
,
1106 client_GetStreamLatency
,
1107 client_GetCurrentPadding
,
1108 client_IsFormatSupported
,
1109 client_GetMixFormat
,
1110 client_GetDevicePeriod
,
1114 client_SetEventHandle
,
1116 client_IsOffloadCapable
,
1117 client_SetClientProperties
,
1118 client_GetBufferSizeLimits
,
1119 client_GetSharedModeEnginePeriod
,
1120 client_GetCurrentSharedModeEnginePeriod
,
1121 client_InitializeSharedAudioStream
,
1124 static HRESULT WINAPI
clock_QueryInterface(IAudioClock
*iface
, REFIID riid
, void **ppv
)
1126 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1128 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1133 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1134 IsEqualIID(riid
, &IID_IAudioClock
))
1136 else if (IsEqualIID(riid
, &IID_IAudioClock2
))
1137 *ppv
= &This
->IAudioClock2_iface
;
1138 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
1139 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1142 return E_NOINTERFACE
;
1145 IUnknown_AddRef((IUnknown
*)*ppv
);
1150 static ULONG WINAPI
clock_AddRef(IAudioClock
*iface
)
1152 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1153 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1156 static ULONG WINAPI
clock_Release(IAudioClock
*iface
)
1158 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1159 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1162 static HRESULT WINAPI
clock_GetFrequency(IAudioClock
*iface
, UINT64
*freq
)
1164 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1165 struct get_frequency_params params
;
1167 TRACE("(%p)->(%p)\n", This
, freq
);
1170 return AUDCLNT_E_NOT_INITIALIZED
;
1172 params
.stream
= This
->stream
;
1175 wine_unix_call(get_frequency
, ¶ms
);
1177 return params
.result
;
1180 static HRESULT WINAPI
clock_GetPosition(IAudioClock
*iface
, UINT64
*pos
, UINT64
*qpctime
)
1182 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1183 struct get_position_params params
;
1185 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
1191 return AUDCLNT_E_NOT_INITIALIZED
;
1193 params
.stream
= This
->stream
;
1194 params
.device
= FALSE
;
1196 params
.qpctime
= qpctime
;
1198 wine_unix_call(get_position
, ¶ms
);
1200 return params
.result
;
1203 static HRESULT WINAPI
clock_GetCharacteristics(IAudioClock
*iface
, DWORD
*chars
)
1205 struct audio_client
*This
= impl_from_IAudioClock(iface
);
1207 TRACE("(%p)->(%p)\n", This
, chars
);
1212 *chars
= AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ
;
1217 const IAudioClockVtbl AudioClock_Vtbl
=
1219 clock_QueryInterface
,
1224 clock_GetCharacteristics
1227 static HRESULT WINAPI
clock2_QueryInterface(IAudioClock2
*iface
, REFIID riid
, void **ppv
)
1229 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1230 return IAudioClock_QueryInterface(&This
->IAudioClock_iface
, riid
, ppv
);
1233 static ULONG WINAPI
clock2_AddRef(IAudioClock2
*iface
)
1235 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1236 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1239 static ULONG WINAPI
clock2_Release(IAudioClock2
*iface
)
1241 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1242 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1245 static HRESULT WINAPI
clock2_GetDevicePosition(IAudioClock2
*iface
, UINT64
*pos
, UINT64
*qpctime
)
1247 struct audio_client
*This
= impl_from_IAudioClock2(iface
);
1248 struct get_position_params params
;
1250 TRACE("(%p)->(%p, %p)\n", This
, pos
, qpctime
);
1256 return AUDCLNT_E_NOT_INITIALIZED
;
1258 params
.stream
= This
->stream
;
1259 params
.device
= TRUE
;
1261 params
.qpctime
= qpctime
;
1263 wine_unix_call(get_position
, ¶ms
);
1265 return params
.result
;
1268 const IAudioClock2Vtbl AudioClock2_Vtbl
=
1270 clock2_QueryInterface
,
1273 clock2_GetDevicePosition
1276 static HRESULT WINAPI
render_QueryInterface(IAudioRenderClient
*iface
, REFIID riid
, void **ppv
)
1278 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1280 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1285 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1286 IsEqualIID(riid
, &IID_IAudioRenderClient
))
1288 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
1289 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1292 return E_NOINTERFACE
;
1295 IUnknown_AddRef((IUnknown
*)*ppv
);
1300 static ULONG WINAPI
render_AddRef(IAudioRenderClient
*iface
)
1302 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1303 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1306 static ULONG WINAPI
render_Release(IAudioRenderClient
*iface
)
1308 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1309 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1312 static HRESULT WINAPI
render_GetBuffer(IAudioRenderClient
*iface
, UINT32 frames
, BYTE
**data
)
1314 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1315 struct get_render_buffer_params params
;
1317 TRACE("(%p)->(%u, %p)\n", This
, frames
, data
);
1323 return AUDCLNT_E_NOT_INITIALIZED
;
1327 params
.stream
= This
->stream
;
1328 params
.frames
= frames
;
1331 wine_unix_call(get_render_buffer
, ¶ms
);
1333 return params
.result
;
1336 static HRESULT WINAPI
render_ReleaseBuffer(IAudioRenderClient
*iface
, UINT32 written_frames
,
1339 struct audio_client
*This
= impl_from_IAudioRenderClient(iface
);
1340 struct release_render_buffer_params params
;
1342 TRACE("(%p)->(%u, %lx)\n", This
, written_frames
, flags
);
1345 return AUDCLNT_E_NOT_INITIALIZED
;
1347 params
.stream
= This
->stream
;
1348 params
.written_frames
= written_frames
;
1349 params
.flags
= flags
;
1351 wine_unix_call(release_render_buffer
, ¶ms
);
1353 return params
.result
;
1356 const IAudioRenderClientVtbl AudioRenderClient_Vtbl
= {
1357 render_QueryInterface
,
1361 render_ReleaseBuffer
1364 static HRESULT WINAPI
streamvolume_QueryInterface(IAudioStreamVolume
*iface
, REFIID riid
,
1367 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
1372 if (IsEqualIID(riid
, &IID_IUnknown
) ||
1373 IsEqualIID(riid
, &IID_IAudioStreamVolume
))
1375 else if (IsEqualIID(riid
, &IID_IMarshal
)) {
1376 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1377 return IUnknown_QueryInterface(This
->marshal
, riid
, ppv
);
1380 return E_NOINTERFACE
;
1383 IUnknown_AddRef((IUnknown
*)*ppv
);
1388 static ULONG WINAPI
streamvolume_AddRef(IAudioStreamVolume
*iface
)
1390 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1391 return IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
1394 static ULONG WINAPI
streamvolume_Release(IAudioStreamVolume
*iface
)
1396 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1397 return IAudioClient3_Release(&This
->IAudioClient3_iface
);
1400 static HRESULT WINAPI
streamvolume_GetChannelCount(IAudioStreamVolume
*iface
, UINT32
*out
)
1402 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1404 TRACE("(%p)->(%p)\n", This
, out
);
1409 *out
= This
->channel_count
;
1414 static HRESULT WINAPI
streamvolume_SetChannelVolume(IAudioStreamVolume
*iface
, UINT32 index
,
1417 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1419 TRACE("(%p)->(%d, %f)\n", This
, index
, level
);
1421 if (level
< 0.f
|| level
> 1.f
)
1422 return E_INVALIDARG
;
1425 return AUDCLNT_E_NOT_INITIALIZED
;
1427 if (index
>= This
->channel_count
)
1428 return E_INVALIDARG
;
1432 This
->vols
[index
] = level
;
1433 set_stream_volumes(This
);
1440 static HRESULT WINAPI
streamvolume_GetChannelVolume(IAudioStreamVolume
*iface
, UINT32 index
,
1443 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1445 TRACE("(%p)->(%d, %p)\n", This
, index
, level
);
1451 return AUDCLNT_E_NOT_INITIALIZED
;
1453 if (index
>= This
->channel_count
)
1454 return E_INVALIDARG
;
1456 *level
= This
->vols
[index
];
1461 static HRESULT WINAPI
streamvolume_SetAllVolumes(IAudioStreamVolume
*iface
, UINT32 count
,
1462 const float *levels
)
1464 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1467 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
1473 return AUDCLNT_E_NOT_INITIALIZED
;
1475 if (count
!= This
->channel_count
)
1476 return E_INVALIDARG
;
1480 for (i
= 0; i
< count
; ++i
)
1481 This
->vols
[i
] = levels
[i
];
1482 set_stream_volumes(This
);
1489 static HRESULT WINAPI
streamvolume_GetAllVolumes(IAudioStreamVolume
*iface
, UINT32 count
,
1492 struct audio_client
*This
= impl_from_IAudioStreamVolume(iface
);
1495 TRACE("(%p)->(%d, %p)\n", This
, count
, levels
);
1501 return AUDCLNT_E_NOT_INITIALIZED
;
1503 if (count
!= This
->channel_count
)
1504 return E_INVALIDARG
;
1508 for (i
= 0; i
< count
; ++i
)
1509 levels
[i
] = This
->vols
[i
];
1516 const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl
=
1518 streamvolume_QueryInterface
,
1519 streamvolume_AddRef
,
1520 streamvolume_Release
,
1521 streamvolume_GetChannelCount
,
1522 streamvolume_SetChannelVolume
,
1523 streamvolume_GetChannelVolume
,
1524 streamvolume_SetAllVolumes
,
1525 streamvolume_GetAllVolumes
1528 HRESULT
AudioClient_Create(GUID
*guid
, IMMDevice
*device
, IAudioClient
**out
)
1530 struct audio_client
*This
;
1535 TRACE("%s %p %p\n", debugstr_guid(guid
), device
, out
);
1539 if (!drvs
.pget_device_name_from_guid(guid
, &name
, &dataflow
))
1540 return AUDCLNT_E_DEVICE_INVALIDATED
;
1542 if (dataflow
!= eRender
&& dataflow
!= eCapture
) {
1544 return E_UNEXPECTED
;
1547 This
= calloc(1, sizeof(*This
));
1550 return E_OUTOFMEMORY
;
1553 This
->device_name
= name
;
1555 This
->IAudioCaptureClient_iface
.lpVtbl
= &AudioCaptureClient_Vtbl
;
1556 This
->IAudioClient3_iface
.lpVtbl
= &AudioClient3_Vtbl
;
1557 This
->IAudioClock_iface
.lpVtbl
= &AudioClock_Vtbl
;
1558 This
->IAudioClock2_iface
.lpVtbl
= &AudioClock2_Vtbl
;
1559 This
->IAudioRenderClient_iface
.lpVtbl
= &AudioRenderClient_Vtbl
;
1560 This
->IAudioStreamVolume_iface
.lpVtbl
= &AudioStreamVolume_Vtbl
;
1562 This
->dataflow
= dataflow
;
1563 This
->parent
= device
;
1565 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient3_iface
, &This
->marshal
);
1567 free(This
->device_name
);
1572 IMMDevice_AddRef(This
->parent
);
1574 *out
= (IAudioClient
*)&This
->IAudioClient3_iface
;
1575 IAudioClient3_AddRef(&This
->IAudioClient3_iface
);