include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / mmdevapi / client.c
blob860c31f4b49c82271da4a6cb1d03b1dea7b6355f
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 "ntstatus.h"
25 #define WIN32_NO_STATUS
27 #include <wchar.h>
29 #include <audiopolicy.h>
30 #include <mmdeviceapi.h>
31 #include <winternl.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
42 WORD wLanguage;
43 WORD wCodePage;
44 } LANGANDCODEPAGE;
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, &params);
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, &params);
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;
129 HRESULT hr;
131 TRACE("Requested duration %lu and period %lu\n", (ULONG)*duration, (ULONG)*period);
133 if (FAILED(hr = get_periods(client, &def_period, &min_period)))
134 return hr;
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;
145 } else {
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;
150 else {
151 if (*period == 0)
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);
171 return hr;
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");
180 break;
181 case WAVE_FORMAT_IEEE_FLOAT:
182 TRACE("WAVE_FORMAT_IEEE_FLOAT");
183 break;
184 case WAVE_FORMAT_EXTENSIBLE:
185 TRACE("WAVE_FORMAT_EXTENSIBLE");
186 break;
187 default:
188 TRACE("Unknown");
189 break;
191 TRACE(")\n");
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, &params);
218 return 0;
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");
227 CloseHandle(event);
228 return E_FAIL;
231 SetThreadPriority(main_loop_thread, THREAD_PRIORITY_TIME_CRITICAL);
232 WaitForSingleObject(event, INFINITE);
233 CloseHandle(event);
236 return S_OK;
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, &params);
250 return 0;
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, &params);
262 return params.result;
265 static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, UINT *len)
267 WCHAR pn[37];
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;
277 LPVOID productname;
278 BOOL found = FALSE;
279 void *data = NULL;
280 unsigned int i;
281 LCID locale;
282 DWORD size;
284 GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
286 size = GetFileVersionInfoSizeW(path, NULL);
287 if (!size)
288 goto skip;
290 data = malloc(size);
291 if (!data)
292 goto skip;
294 if (!GetFileVersionInfoW(path, 0, size, data))
295 goto skip;
297 if (!VerQueryValueW(data, L"\\VarFileInfo\\Translation", (LPVOID *)&translate, &translate_size))
298 goto skip;
300 /* No translations found. */
301 if (translate_size < sizeof(LANGANDCODEPAGE))
302 goto skip;
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)) {
313 found = TRUE;
314 break;
318 if (!found) {
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)) {
322 found = TRUE;
323 break;
328 if (!found) {
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)) {
332 found = TRUE;
333 break;
338 if (!found) {
339 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
340 if (query_productname(data, &translate[i], &productname, &productname_size)) {
341 found = TRUE;
342 break;
346 skip:
347 if (found) {
348 name = wcsdup(productname);
349 free(data);
350 return name;
353 free(data);
355 name = wcsrchr(path, '\\');
356 if (!name)
357 name = path;
358 else
359 name++;
361 return wcsdup(name);
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;
372 WCHAR *name;
374 if (!fmt)
375 return E_POINTER;
377 dump_fmt(fmt);
379 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
380 return E_INVALIDARG;
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);
393 return E_INVALIDARG;
396 sessions_lock();
398 if (client->stream) {
399 sessions_unlock();
400 return AUDCLNT_E_ALREADY_INITIALIZED;
403 if (FAILED(params.result = main_loop_start())) {
404 sessions_unlock();
405 return params.result;
408 if (flags & AUDCLNT_STREAMFLAGS_LOOPBACK) {
409 struct get_loopback_capture_device_params params;
411 if (client->dataflow != eRender) {
412 sessions_unlock();
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, &params);
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, &params);
427 free(name);
428 if (FAILED(params.result)) {
429 sessions_unlock();
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))) {
441 sessions_unlock();
442 return params.result;
445 params.name = name = get_application_name();
446 params.device = client->device_name;
447 params.flow = client->dataflow;
448 params.share = mode;
449 params.flags = flags;
450 params.duration = duration;
451 params.period = period;
452 params.fmt = fmt;
453 params.channel_count = &channel_count;
454 params.stream = &stream;
456 wine_unix_call(create_stream, &params);
458 free(name);
460 if (FAILED(params.result)) {
461 sessions_unlock();
462 return params.result;
465 if (!(client->vols = malloc(channel_count * sizeof(*client->vols)))) {
466 params.result = E_OUTOFMEMORY;
467 goto exit;
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);
475 exit:
476 if (FAILED(params.result)) {
477 stream_release(stream, NULL);
478 free(client->vols);
479 client->vols = NULL;
480 } else {
481 list_add_tail(&client->session->clients, &client->entry);
482 client->stream = stream;
483 client->channel_count = channel_count;
484 set_stream_volumes(client);
487 sessions_unlock();
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);
498 if (!ppv)
499 return E_POINTER;
501 if (IsEqualIID(riid, &IID_IUnknown) ||
502 IsEqualIID(riid, &IID_IAudioCaptureClient))
503 *ppv = iface;
504 else if (IsEqualIID(riid, &IID_IMarshal)) {
505 return IUnknown_QueryInterface(This->marshal, riid, ppv);
506 } else {
507 *ppv = NULL;
508 return E_NOINTERFACE;
511 IUnknown_AddRef((IUnknown *)*ppv);
513 return S_OK;
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);
536 if (!data)
537 return E_POINTER;
539 *data = NULL;
541 if (!frames || !flags)
542 return E_POINTER;
544 if (!This->stream)
545 return AUDCLNT_E_NOT_INITIALIZED;
547 params.stream = This->stream;
548 params.data = data;
549 params.frames = frames;
550 params.flags = (UINT *)flags;
551 params.devpos = devpos;
552 params.qpcpos = qpcpos;
554 wine_unix_call(get_capture_buffer, &params);
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);
566 if (!This->stream)
567 return AUDCLNT_E_NOT_INITIALIZED;
569 params.stream = This->stream;
570 params.done = done;
572 wine_unix_call(release_capture_buffer, &params);
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);
584 if (!frames)
585 return E_POINTER;
587 if (!This->stream)
588 return AUDCLNT_E_NOT_INITIALIZED;
590 params.stream = This->stream;
591 params.frames = frames;
593 wine_unix_call(get_next_packet_size, &params);
595 return params.result;
598 const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
600 capture_QueryInterface,
601 capture_AddRef,
602 capture_Release,
603 capture_GetBuffer,
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);
612 if (!ppv)
613 return E_POINTER;
615 if (IsEqualIID(riid, &IID_IUnknown) ||
616 IsEqualIID(riid, &IID_IAudioClient) ||
617 IsEqualIID(riid, &IID_IAudioClient2) ||
618 IsEqualIID(riid, &IID_IAudioClient3))
619 *ppv = iface;
620 else if(IsEqualIID(riid, &IID_IMarshal)) {
621 struct audio_client *This = impl_from_IAudioClient3(iface);
622 return IUnknown_QueryInterface(This->marshal, riid, ppv);
623 } else {
624 *ppv = NULL;
625 return E_NOINTERFACE;
628 IUnknown_AddRef((IUnknown *)*ppv);
630 return S_OK;
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);
638 return 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);
647 if (!ref) {
648 IAudioClient3_Stop(iface);
649 IMMDevice_Release(This->parent);
650 IUnknown_Release(This->marshal);
652 if (This->session) {
653 sessions_lock();
654 list_remove(&This->entry);
655 sessions_unlock();
658 free(This->vols);
660 if (This->stream)
661 stream_release(This->stream, This->timer_thread);
663 free(This->device_name);
664 free(This);
667 return ref;
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);
690 if (!out)
691 return E_POINTER;
693 if (!This->stream)
694 return AUDCLNT_E_NOT_INITIALIZED;
696 params.stream = This->stream;
697 params.frames = out;
699 wine_unix_call(get_buffer_size, &params);
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);
711 if (!latency)
712 return E_POINTER;
714 if (!This->stream)
715 return AUDCLNT_E_NOT_INITIALIZED;
717 params.stream = This->stream;
718 params.latency = latency;
720 wine_unix_call(get_latency, &params);
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);
732 if (!out)
733 return E_POINTER;
735 if (!This->stream)
736 return AUDCLNT_E_NOT_INITIALIZED;
738 params.stream = This->stream;
739 params.padding = out;
741 wine_unix_call(get_current_padding, &params);
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);
754 if (fmt)
755 dump_fmt(fmt);
757 params.device = This->device_name;
758 params.flow = This->dataflow;
759 params.share = mode;
760 params.fmt_in = fmt;
761 params.fmt_out = NULL;
763 if (out) {
764 *out = NULL;
765 if (mode == AUDCLNT_SHAREMODE_SHARED)
766 params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
769 wine_unix_call(is_format_supported, &params);
771 if (params.result == S_FALSE)
772 *out = &params.fmt_out->Format;
773 else
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);
786 if (!pwfx)
787 return E_POINTER;
789 *pwfx = NULL;
791 params.device = This->device_name;
792 params.flow = This->dataflow;
793 params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
794 if (!params.fmt)
795 return E_OUTOFMEMORY;
797 wine_unix_call(get_mix_format, &params);
799 if (SUCCEEDED(params.result)) {
800 *pwfx = &params.fmt->Format;
801 dump_fmt(*pwfx);
802 } else
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)
816 return E_POINTER;
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);
828 sessions_lock();
830 if (!This->stream) {
831 sessions_unlock();
832 return AUDCLNT_E_NOT_INITIALIZED;
835 params.stream = This->stream;
836 wine_unix_call(start, &params);
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);
841 else {
842 IAudioClient3_Stop(&This->IAudioClient3_iface);
843 params.result = E_FAIL;
847 sessions_unlock();
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);
859 if (!This->stream)
860 return AUDCLNT_E_NOT_INITIALIZED;
862 params.stream = This->stream;
864 wine_unix_call(stop, &params);
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);
876 if (!This->stream)
877 return AUDCLNT_E_NOT_INITIALIZED;
879 params.stream = This->stream;
881 wine_unix_call(reset, &params);
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);
893 if (!event)
894 return E_INVALIDARG;
896 if (!This->stream)
897 return AUDCLNT_E_NOT_INITIALIZED;
899 params.stream = This->stream;
900 params.event = event;
902 wine_unix_call(set_event_handle, &params);
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);
910 HRESULT hr;
912 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
914 if (!ppv)
915 return E_POINTER;
917 *ppv = NULL;
919 sessions_lock();
921 if (!This->stream) {
922 hr = AUDCLNT_E_NOT_INITIALIZED;
923 goto exit;
926 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
927 if (This->dataflow != eRender) {
928 hr = AUDCLNT_E_WRONG_ENDPOINT_TYPE;
929 goto exit;
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;
937 goto exit;
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;
952 if (new_session) {
953 This->session_wrapper = session_wrapper_create(This);
954 if (!This->session_wrapper) {
955 hr = E_OUTOFMEMORY;
956 goto exit;
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;
967 if (!new_session)
968 IUnknown_AddRef((IUnknown *)*ppv);
969 } else {
970 FIXME("stub %s\n", debugstr_guid(riid));
971 hr = E_NOINTERFACE;
972 goto exit;
975 hr = S_OK;
976 exit:
977 sessions_unlock();
979 return hr;
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)
990 return E_INVALIDARG;
992 *offload_capable = FALSE;
994 return S_OK;
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);
1005 if (!legacy_prop)
1006 return E_POINTER;
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,
1011 prop->Options);
1012 } else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)) {
1013 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n", legacy_prop->bIsOffload,
1014 legacy_prop->eCategory);
1015 } else {
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;
1023 return S_OK;
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);
1032 return E_NOTIMPL;
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;
1044 HRESULT hr;
1046 TRACE("(%p)->(%p, %p, %p, %p, %p)\n",
1047 This, format, default_period_frames,
1048 unit_period_frames, min_period_frames,
1049 max_period_frames);
1051 if (FAILED(hr = get_periods(This, &def_period, &min_period)))
1052 return hr;
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;
1059 return hr;
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);
1067 UINT32 dummy;
1068 HRESULT hr;
1070 TRACE("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1072 if (!cur_format || !cur_period_frames)
1073 return E_POINTER;
1075 if (FAILED(hr = client_GetMixFormat(iface, cur_format)))
1076 return hr;
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));
1091 if (!format)
1092 return E_POINTER;
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,
1102 client_AddRef,
1103 client_Release,
1104 client_Initialize,
1105 client_GetBufferSize,
1106 client_GetStreamLatency,
1107 client_GetCurrentPadding,
1108 client_IsFormatSupported,
1109 client_GetMixFormat,
1110 client_GetDevicePeriod,
1111 client_Start,
1112 client_Stop,
1113 client_Reset,
1114 client_SetEventHandle,
1115 client_GetService,
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);
1130 if (!ppv)
1131 return E_POINTER;
1133 if (IsEqualIID(riid, &IID_IUnknown) ||
1134 IsEqualIID(riid, &IID_IAudioClock))
1135 *ppv = iface;
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);
1140 } else {
1141 *ppv = NULL;
1142 return E_NOINTERFACE;
1145 IUnknown_AddRef((IUnknown *)*ppv);
1147 return S_OK;
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);
1169 if (!This->stream)
1170 return AUDCLNT_E_NOT_INITIALIZED;
1172 params.stream = This->stream;
1173 params.freq = freq;
1175 wine_unix_call(get_frequency, &params);
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);
1187 if (!pos)
1188 return E_POINTER;
1190 if (!This->stream)
1191 return AUDCLNT_E_NOT_INITIALIZED;
1193 params.stream = This->stream;
1194 params.device = FALSE;
1195 params.pos = pos;
1196 params.qpctime = qpctime;
1198 wine_unix_call(get_position, &params);
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);
1209 if (!chars)
1210 return E_POINTER;
1212 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
1214 return S_OK;
1217 const IAudioClockVtbl AudioClock_Vtbl =
1219 clock_QueryInterface,
1220 clock_AddRef,
1221 clock_Release,
1222 clock_GetFrequency,
1223 clock_GetPosition,
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);
1252 if (!pos)
1253 return E_POINTER;
1255 if (!This->stream)
1256 return AUDCLNT_E_NOT_INITIALIZED;
1258 params.stream = This->stream;
1259 params.device = TRUE;
1260 params.pos = pos;
1261 params.qpctime = qpctime;
1263 wine_unix_call(get_position, &params);
1265 return params.result;
1268 const IAudioClock2Vtbl AudioClock2_Vtbl =
1270 clock2_QueryInterface,
1271 clock2_AddRef,
1272 clock2_Release,
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);
1282 if (!ppv)
1283 return E_POINTER;
1285 if (IsEqualIID(riid, &IID_IUnknown) ||
1286 IsEqualIID(riid, &IID_IAudioRenderClient))
1287 *ppv = iface;
1288 else if (IsEqualIID(riid, &IID_IMarshal)) {
1289 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1290 } else {
1291 *ppv = NULL;
1292 return E_NOINTERFACE;
1295 IUnknown_AddRef((IUnknown *)*ppv);
1297 return S_OK;
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);
1319 if (!data)
1320 return E_POINTER;
1322 if (!This->stream)
1323 return AUDCLNT_E_NOT_INITIALIZED;
1325 *data = NULL;
1327 params.stream = This->stream;
1328 params.frames = frames;
1329 params.data = data;
1331 wine_unix_call(get_render_buffer, &params);
1333 return params.result;
1336 static HRESULT WINAPI render_ReleaseBuffer(IAudioRenderClient *iface, UINT32 written_frames,
1337 DWORD flags)
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);
1344 if (!This->stream)
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, &params);
1353 return params.result;
1356 const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1357 render_QueryInterface,
1358 render_AddRef,
1359 render_Release,
1360 render_GetBuffer,
1361 render_ReleaseBuffer
1364 static HRESULT WINAPI streamvolume_QueryInterface(IAudioStreamVolume *iface, REFIID riid,
1365 void **ppv)
1367 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1369 if (!ppv)
1370 return E_POINTER;
1372 if (IsEqualIID(riid, &IID_IUnknown) ||
1373 IsEqualIID(riid, &IID_IAudioStreamVolume))
1374 *ppv = iface;
1375 else if (IsEqualIID(riid, &IID_IMarshal)) {
1376 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1377 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1378 } else {
1379 *ppv = NULL;
1380 return E_NOINTERFACE;
1383 IUnknown_AddRef((IUnknown *)*ppv);
1385 return S_OK;
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);
1406 if (!out)
1407 return E_POINTER;
1409 *out = This->channel_count;
1411 return S_OK;
1414 static HRESULT WINAPI streamvolume_SetChannelVolume(IAudioStreamVolume *iface, UINT32 index,
1415 float level)
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;
1424 if (!This->stream)
1425 return AUDCLNT_E_NOT_INITIALIZED;
1427 if (index >= This->channel_count)
1428 return E_INVALIDARG;
1430 sessions_lock();
1432 This->vols[index] = level;
1433 set_stream_volumes(This);
1435 sessions_unlock();
1437 return S_OK;
1440 static HRESULT WINAPI streamvolume_GetChannelVolume(IAudioStreamVolume *iface, UINT32 index,
1441 float *level)
1443 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1445 TRACE("(%p)->(%d, %p)\n", This, index, level);
1447 if (!level)
1448 return E_POINTER;
1450 if (!This->stream)
1451 return AUDCLNT_E_NOT_INITIALIZED;
1453 if (index >= This->channel_count)
1454 return E_INVALIDARG;
1456 *level = This->vols[index];
1458 return S_OK;
1461 static HRESULT WINAPI streamvolume_SetAllVolumes(IAudioStreamVolume *iface, UINT32 count,
1462 const float *levels)
1464 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1465 unsigned int i;
1467 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1469 if (!levels)
1470 return E_POINTER;
1472 if (!This->stream)
1473 return AUDCLNT_E_NOT_INITIALIZED;
1475 if (count != This->channel_count)
1476 return E_INVALIDARG;
1478 sessions_lock();
1480 for (i = 0; i < count; ++i)
1481 This->vols[i] = levels[i];
1482 set_stream_volumes(This);
1484 sessions_unlock();
1486 return S_OK;
1489 static HRESULT WINAPI streamvolume_GetAllVolumes(IAudioStreamVolume *iface, UINT32 count,
1490 float *levels)
1492 struct audio_client *This = impl_from_IAudioStreamVolume(iface);
1493 unsigned int i;
1495 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1497 if (!levels)
1498 return E_POINTER;
1500 if (!This->stream)
1501 return AUDCLNT_E_NOT_INITIALIZED;
1503 if (count != This->channel_count)
1504 return E_INVALIDARG;
1506 sessions_lock();
1508 for (i = 0; i < count; ++i)
1509 levels[i] = This->vols[i];
1511 sessions_unlock();
1513 return S_OK;
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;
1531 char *name;
1532 EDataFlow dataflow;
1533 HRESULT hr;
1535 TRACE("%s %p %p\n", debugstr_guid(guid), device, out);
1537 *out = NULL;
1539 if (!drvs.pget_device_name_from_guid(guid, &name, &dataflow))
1540 return AUDCLNT_E_DEVICE_INVALIDATED;
1542 if (dataflow != eRender && dataflow != eCapture) {
1543 free(name);
1544 return E_UNEXPECTED;
1547 This = calloc(1, sizeof(*This));
1548 if (!This) {
1549 free(name);
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);
1566 if (FAILED(hr)) {
1567 free(This->device_name);
1568 free(This);
1569 return hr;
1572 IMMDevice_AddRef(This->parent);
1574 *out = (IAudioClient *)&This->IAudioClient3_iface;
1575 IAudioClient3_AddRef(&This->IAudioClient3_iface);
1577 return S_OK;