mmdevapi: Use CRT allocation functions.
[wine.git] / dlls / mmdevapi / client.c
blob8146f8a53b49bf95665dafb87f0cc781ac4d2fad
1 /*
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
22 #define COBJMACROS
24 #include <wchar.h>
26 #include <audiopolicy.h>
27 #include <mmdeviceapi.h>
28 #include <winternl.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
39 WORD wLanguage;
40 WORD wCodePage;
41 } LANGANDCODEPAGE;
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, &params);
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");
108 break;
109 case WAVE_FORMAT_IEEE_FLOAT:
110 TRACE("WAVE_FORMAT_IEEE_FLOAT");
111 break;
112 case WAVE_FORMAT_EXTENSIBLE:
113 TRACE("WAVE_FORMAT_EXTENSIBLE");
114 break;
115 default:
116 TRACE("Unknown");
117 break;
119 TRACE(")\n");
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, &params);
146 return 0;
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");
155 CloseHandle(event);
156 return E_FAIL;
159 SetThreadPriority(main_loop_thread, THREAD_PRIORITY_TIME_CRITICAL);
160 WaitForSingleObject(event, INFINITE);
161 CloseHandle(event);
164 return S_OK;
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, &params);
178 return 0;
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, &params);
190 return params.result;
193 static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, UINT *len)
195 WCHAR pn[37];
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;
205 LPVOID productname;
206 BOOL found = FALSE;
207 void *data = NULL;
208 unsigned int i;
209 LCID locale;
210 DWORD size;
212 GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
214 size = GetFileVersionInfoSizeW(path, NULL);
215 if (!size)
216 goto skip;
218 data = malloc(size);
219 if (!data)
220 goto skip;
222 if (!GetFileVersionInfoW(path, 0, size, data))
223 goto skip;
225 if (!VerQueryValueW(data, L"\\VarFileInfo\\Translation", (LPVOID *)&translate, &translate_size))
226 goto skip;
228 /* No translations found. */
229 if (translate_size < sizeof(LANGANDCODEPAGE))
230 goto skip;
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)) {
241 found = TRUE;
242 break;
246 if (!found) {
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)) {
250 found = TRUE;
251 break;
256 if (!found) {
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)) {
260 found = TRUE;
261 break;
266 if (!found) {
267 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
268 if (query_productname(data, &translate[i], &productname, &productname_size)) {
269 found = TRUE;
270 break;
274 skip:
275 if (found) {
276 name = wcsdup(productname);
277 free(data);
278 return name;
281 free(data);
283 name = wcsrchr(path, '\\');
284 if (!name)
285 name = path;
286 else
287 name++;
289 return wcsdup(name);
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);
298 if (!ppv)
299 return E_POINTER;
301 if (IsEqualIID(riid, &IID_IUnknown) ||
302 IsEqualIID(riid, &IID_IAudioCaptureClient))
303 *ppv = iface;
304 else if (IsEqualIID(riid, &IID_IMarshal)) {
305 return IUnknown_QueryInterface(This->marshal, riid, ppv);
306 } else {
307 *ppv = NULL;
308 return E_NOINTERFACE;
311 IUnknown_AddRef((IUnknown *)*ppv);
313 return S_OK;
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);
336 if (!data)
337 return E_POINTER;
339 *data = NULL;
341 if (!frames || !flags)
342 return E_POINTER;
344 if (!This->stream)
345 return AUDCLNT_E_NOT_INITIALIZED;
347 params.stream = This->stream;
348 params.data = data;
349 params.frames = frames;
350 params.flags = (UINT *)flags;
351 params.devpos = devpos;
352 params.qpcpos = qpcpos;
354 wine_unix_call(get_capture_buffer, &params);
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);
366 if (!This->stream)
367 return AUDCLNT_E_NOT_INITIALIZED;
369 params.stream = This->stream;
370 params.done = done;
372 wine_unix_call(release_capture_buffer, &params);
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);
384 if (!frames)
385 return E_POINTER;
387 if (!This->stream)
388 return AUDCLNT_E_NOT_INITIALIZED;
390 params.stream = This->stream;
391 params.frames = frames;
393 wine_unix_call(get_next_packet_size, &params);
395 return params.result;
398 const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
400 capture_QueryInterface,
401 capture_AddRef,
402 capture_Release,
403 capture_GetBuffer,
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);
412 if (!ppv)
413 return E_POINTER;
415 if (IsEqualIID(riid, &IID_IUnknown) ||
416 IsEqualIID(riid, &IID_IAudioClient) ||
417 IsEqualIID(riid, &IID_IAudioClient2) ||
418 IsEqualIID(riid, &IID_IAudioClient3))
419 *ppv = iface;
420 else if(IsEqualIID(riid, &IID_IMarshal)) {
421 struct audio_client *This = impl_from_IAudioClient3(iface);
422 return IUnknown_QueryInterface(This->marshal, riid, ppv);
423 } else {
424 *ppv = NULL;
425 return E_NOINTERFACE;
428 IUnknown_AddRef((IUnknown *)*ppv);
430 return S_OK;
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);
438 return 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);
447 if (!ref) {
448 IAudioClient3_Stop(iface);
449 IMMDevice_Release(This->parent);
450 IUnknown_Release(This->marshal);
452 if (This->session) {
453 sessions_lock();
454 list_remove(&This->entry);
455 sessions_unlock();
458 free(This->vols);
460 if (This->stream)
461 stream_release(This->stream, This->timer_thread);
463 free(This);
466 return ref;
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;
477 WCHAR *name;
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));
483 if (!fmt)
484 return E_POINTER;
486 dump_fmt(fmt);
488 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
489 return E_INVALIDARG;
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);
502 return E_INVALIDARG;
505 sessions_lock();
507 if (This->stream) {
508 sessions_unlock();
509 return AUDCLNT_E_ALREADY_INITIALIZED;
512 if (FAILED(params.result = main_loop_start())) {
513 sessions_unlock();
514 return params.result;
517 params.name = name = get_application_name();
518 params.device = This->device_name;
519 params.flow = This->dataflow;
520 params.share = mode;
521 params.flags = flags;
522 params.duration = duration;
523 params.period = period;
524 params.fmt = fmt;
525 params.channel_count = &channel_count;
526 params.stream = &stream;
528 wine_unix_call(create_stream, &params);
530 free(name);
532 if (FAILED(params.result)) {
533 sessions_unlock();
534 return params.result;
537 if (!(This->vols = malloc(channel_count * sizeof(*This->vols)))) {
538 params.result = E_OUTOFMEMORY;
539 goto exit;
542 for (i = 0; i < channel_count; i++)
543 This->vols[i] = 1.f;
545 params.result = get_audio_session(sessionguid, This->parent, channel_count, &This->session);
547 exit:
548 if (FAILED(params.result)) {
549 stream_release(stream, NULL);
550 free(This->vols);
551 This->vols = NULL;
552 } else {
553 list_add_tail(&This->session->clients, &This->entry);
554 This->stream = stream;
555 This->channel_count = channel_count;
556 set_stream_volumes(This);
559 sessions_unlock();
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);
571 if (!out)
572 return E_POINTER;
574 if (!This->stream)
575 return AUDCLNT_E_NOT_INITIALIZED;
577 params.stream = This->stream;
578 params.frames = out;
580 wine_unix_call(get_buffer_size, &params);
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);
592 if (!latency)
593 return E_POINTER;
595 if (!This->stream)
596 return AUDCLNT_E_NOT_INITIALIZED;
598 params.stream = This->stream;
599 params.latency = latency;
601 wine_unix_call(get_latency, &params);
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);
613 if (!out)
614 return E_POINTER;
616 if (!This->stream)
617 return AUDCLNT_E_NOT_INITIALIZED;
619 params.stream = This->stream;
620 params.padding = out;
622 wine_unix_call(get_current_padding, &params);
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);
635 if (fmt)
636 dump_fmt(fmt);
638 params.device = This->device_name;
639 params.flow = This->dataflow;
640 params.share = mode;
641 params.fmt_in = fmt;
642 params.fmt_out = NULL;
644 if (out) {
645 *out = NULL;
646 if (mode == AUDCLNT_SHAREMODE_SHARED)
647 params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
650 wine_unix_call(is_format_supported, &params);
652 if (params.result == S_FALSE)
653 *out = &params.fmt_out->Format;
654 else
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);
667 if (!pwfx)
668 return E_POINTER;
670 *pwfx = NULL;
672 params.device = This->device_name;
673 params.flow = This->dataflow;
674 params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
675 if (!params.fmt)
676 return E_OUTOFMEMORY;
678 wine_unix_call(get_mix_format, &params);
680 if (SUCCEEDED(params.result)) {
681 *pwfx = &params.fmt->Format;
682 dump_fmt(*pwfx);
683 } else
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)
698 return E_POINTER;
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, &params);
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);
717 sessions_lock();
719 if (!This->stream) {
720 sessions_unlock();
721 return AUDCLNT_E_NOT_INITIALIZED;
724 params.stream = This->stream;
725 wine_unix_call(start, &params);
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);
730 else {
731 IAudioClient3_Stop(&This->IAudioClient3_iface);
732 params.result = E_FAIL;
736 sessions_unlock();
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);
748 if (!This->stream)
749 return AUDCLNT_E_NOT_INITIALIZED;
751 params.stream = This->stream;
753 wine_unix_call(stop, &params);
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);
765 if (!This->stream)
766 return AUDCLNT_E_NOT_INITIALIZED;
768 params.stream = This->stream;
770 wine_unix_call(reset, &params);
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);
782 if (!event)
783 return E_INVALIDARG;
785 if (!This->stream)
786 return AUDCLNT_E_NOT_INITIALIZED;
788 params.stream = This->stream;
789 params.event = event;
791 wine_unix_call(set_event_handle, &params);
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);
799 HRESULT hr;
801 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
803 if (!ppv)
804 return E_POINTER;
806 *ppv = NULL;
808 sessions_lock();
810 if (!This->stream) {
811 hr = AUDCLNT_E_NOT_INITIALIZED;
812 goto exit;
815 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
816 if (This->dataflow != eRender) {
817 hr = AUDCLNT_E_WRONG_ENDPOINT_TYPE;
818 goto exit;
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;
826 goto exit;
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;
841 if (new_session) {
842 This->session_wrapper = session_wrapper_create(This);
843 if (!This->session_wrapper) {
844 hr = E_OUTOFMEMORY;
845 goto exit;
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;
856 if (!new_session)
857 IUnknown_AddRef((IUnknown *)*ppv);
858 } else {
859 FIXME("stub %s\n", debugstr_guid(riid));
860 hr = E_NOINTERFACE;
861 goto exit;
864 hr = S_OK;
865 exit:
866 sessions_unlock();
868 return hr;
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)
879 return E_INVALIDARG;
881 *offload_capable = FALSE;
883 return S_OK;
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);
894 if (!legacy_prop)
895 return E_POINTER;
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,
900 prop->Options);
901 } else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)) {
902 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n", legacy_prop->bIsOffload,
903 legacy_prop->eCategory);
904 } else {
905 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
906 return E_INVALIDARG;
909 if (legacy_prop->bIsOffload)
910 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
912 return S_OK;
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);
921 return E_NOTIMPL;
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,
934 max_period_frames);
935 return E_NOTIMPL;
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);
944 return E_NOTIMPL;
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));
954 return E_NOTIMPL;
957 const IAudioClient3Vtbl AudioClient3_Vtbl =
959 client_QueryInterface,
960 client_AddRef,
961 client_Release,
962 client_Initialize,
963 client_GetBufferSize,
964 client_GetStreamLatency,
965 client_GetCurrentPadding,
966 client_IsFormatSupported,
967 client_GetMixFormat,
968 client_GetDevicePeriod,
969 client_Start,
970 client_Stop,
971 client_Reset,
972 client_SetEventHandle,
973 client_GetService,
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);
988 if (!ppv)
989 return E_POINTER;
991 if (IsEqualIID(riid, &IID_IUnknown) ||
992 IsEqualIID(riid, &IID_IAudioClock))
993 *ppv = iface;
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);
998 } else {
999 *ppv = NULL;
1000 return E_NOINTERFACE;
1003 IUnknown_AddRef((IUnknown *)*ppv);
1005 return S_OK;
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);
1027 if (!This->stream)
1028 return AUDCLNT_E_NOT_INITIALIZED;
1030 params.stream = This->stream;
1031 params.freq = freq;
1033 wine_unix_call(get_frequency, &params);
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);
1045 if (!pos)
1046 return E_POINTER;
1048 if (!This->stream)
1049 return AUDCLNT_E_NOT_INITIALIZED;
1051 params.stream = This->stream;
1052 params.device = FALSE;
1053 params.pos = pos;
1054 params.qpctime = qpctime;
1056 wine_unix_call(get_position, &params);
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);
1067 if (!chars)
1068 return E_POINTER;
1070 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
1072 return S_OK;
1075 const IAudioClockVtbl AudioClock_Vtbl =
1077 clock_QueryInterface,
1078 clock_AddRef,
1079 clock_Release,
1080 clock_GetFrequency,
1081 clock_GetPosition,
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);
1110 if (!pos)
1111 return E_POINTER;
1113 if (!This->stream)
1114 return AUDCLNT_E_NOT_INITIALIZED;
1116 params.stream = This->stream;
1117 params.device = TRUE;
1118 params.pos = pos;
1119 params.qpctime = qpctime;
1121 wine_unix_call(get_position, &params);
1123 return params.result;
1126 const IAudioClock2Vtbl AudioClock2_Vtbl =
1128 clock2_QueryInterface,
1129 clock2_AddRef,
1130 clock2_Release,
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);
1140 if (!ppv)
1141 return E_POINTER;
1143 if (IsEqualIID(riid, &IID_IUnknown) ||
1144 IsEqualIID(riid, &IID_IAudioRenderClient))
1145 *ppv = iface;
1146 else if (IsEqualIID(riid, &IID_IMarshal)) {
1147 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1148 } else {
1149 *ppv = NULL;
1150 return E_NOINTERFACE;
1153 IUnknown_AddRef((IUnknown *)*ppv);
1155 return S_OK;
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);
1177 if (!data)
1178 return E_POINTER;
1180 if (!This->stream)
1181 return AUDCLNT_E_NOT_INITIALIZED;
1183 *data = NULL;
1185 params.stream = This->stream;
1186 params.frames = frames;
1187 params.data = data;
1189 wine_unix_call(get_render_buffer, &params);
1191 return params.result;
1194 static HRESULT WINAPI render_ReleaseBuffer(IAudioRenderClient *iface, UINT32 written_frames,
1195 DWORD flags)
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);
1202 if (!This->stream)
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, &params);
1211 return params.result;
1214 const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1215 render_QueryInterface,
1216 render_AddRef,
1217 render_Release,
1218 render_GetBuffer,
1219 render_ReleaseBuffer
1222 static HRESULT WINAPI streamvolume_QueryInterface(IAudioStreamVolume *iface, REFIID riid,
1223 void **ppv)
1225 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1227 if (!ppv)
1228 return E_POINTER;
1230 if (IsEqualIID(riid, &IID_IUnknown) ||
1231 IsEqualIID(riid, &IID_IAudioStreamVolume))
1232 *ppv = iface;
1233 else if (IsEqualIID(riid, &IID_IMarshal)) {
1234 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1235 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1236 } else {
1237 *ppv = NULL;
1238 return E_NOINTERFACE;
1241 IUnknown_AddRef((IUnknown *)*ppv);
1243 return S_OK;
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);
1264 if (!out)
1265 return E_POINTER;
1267 *out = This->channel_count;
1269 return S_OK;
1272 static HRESULT WINAPI streamvolume_SetChannelVolume(IAudioStreamVolume *iface, UINT32 index,
1273 float level)
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;
1282 if (!This->stream)
1283 return AUDCLNT_E_NOT_INITIALIZED;
1285 if (index >= This->channel_count)
1286 return E_INVALIDARG;
1288 sessions_lock();
1290 This->vols[index] = level;
1291 set_stream_volumes(This);
1293 sessions_unlock();
1295 return S_OK;
1298 static HRESULT WINAPI streamvolume_GetChannelVolume(IAudioStreamVolume *iface, UINT32 index,
1299 float *level)
1301 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1303 TRACE("(%p)->(%d, %p)\n", This, index, level);
1305 if (!level)
1306 return E_POINTER;
1308 if (!This->stream)
1309 return AUDCLNT_E_NOT_INITIALIZED;
1311 if (index >= This->channel_count)
1312 return E_INVALIDARG;
1314 *level = This->vols[index];
1316 return S_OK;
1319 static HRESULT WINAPI streamvolume_SetAllVolumes(IAudioStreamVolume *iface, UINT32 count,
1320 const float *levels)
1322 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1323 unsigned int i;
1325 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1327 if (!levels)
1328 return E_POINTER;
1330 if (!This->stream)
1331 return AUDCLNT_E_NOT_INITIALIZED;
1333 if (count != This->channel_count)
1334 return E_INVALIDARG;
1336 sessions_lock();
1338 for (i = 0; i < count; ++i)
1339 This->vols[i] = levels[i];
1340 set_stream_volumes(This);
1342 sessions_unlock();
1344 return S_OK;
1347 static HRESULT WINAPI streamvolume_GetAllVolumes(IAudioStreamVolume *iface, UINT32 count,
1348 float *levels)
1350 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1351 unsigned int i;
1353 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1355 if (!levels)
1356 return E_POINTER;
1358 if (!This->stream)
1359 return AUDCLNT_E_NOT_INITIALIZED;
1361 if (count != This->channel_count)
1362 return E_INVALIDARG;
1364 sessions_lock();
1366 for (i = 0; i < count; ++i)
1367 levels[i] = This->vols[i];
1369 sessions_unlock();
1371 return S_OK;
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;
1389 char *name;
1390 EDataFlow dataflow;
1391 size_t size;
1392 HRESULT hr;
1394 TRACE("%s %p %p\n", debugstr_guid(guid), device, out);
1396 *out = NULL;
1398 if (!drvs.pget_device_name_from_guid(guid, &name, &dataflow))
1399 return AUDCLNT_E_DEVICE_INVALIDATED;
1401 if (dataflow != eRender && dataflow != eCapture) {
1402 free(name);
1403 return E_UNEXPECTED;
1406 size = strlen(name) + 1;
1407 This = calloc(1, FIELD_OFFSET(struct audio_client, device_name[size]));
1408 if (!This) {
1409 free(name);
1410 return E_OUTOFMEMORY;
1413 memcpy(This->device_name, name, size);
1414 free(name);
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);
1427 if (FAILED(hr)) {
1428 free(This);
1429 return hr;
1432 IMMDevice_AddRef(This->parent);
1434 *out = (IAudioClient *)&This->IAudioClient3_iface;
1435 IAudioClient3_AddRef(&This->IAudioClient3_iface);
1437 return S_OK;