mmdevapi: Move test_connect handling into mmdevapi.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob20fa08d3fa70ab2953857728d9c685c19868391c
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 <stdarg.h>
25 #include <assert.h>
26 #include <wchar.h>
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winternl.h"
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unixlib.h"
35 #include "ole2.h"
36 #include "mimeole.h"
37 #include "dshow.h"
38 #include "dsound.h"
39 #include "propsys.h"
40 #include "propkey.h"
42 #include "initguid.h"
43 #include "ks.h"
44 #include "ksmedia.h"
45 #include "mmdeviceapi.h"
46 #include "audioclient.h"
47 #include "endpointvolume.h"
48 #include "audiopolicy.h"
50 #include "../mmdevapi/unixlib.h"
52 WINE_DEFAULT_DEBUG_CHANNEL(pulse);
54 #define MAX_PULSE_NAME_LEN 256
56 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
58 static HANDLE pulse_thread;
59 static struct list g_sessions = LIST_INIT(g_sessions);
60 static struct list g_devices_cache = LIST_INIT(g_devices_cache);
62 struct device_cache {
63 struct list entry;
64 GUID guid;
65 EDataFlow dataflow;
66 char pulse_name[0];
69 static GUID pulse_render_guid =
70 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
71 static GUID pulse_capture_guid =
72 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
74 static const WCHAR *drv_key_devicesW = L"Software\\Wine\\Drivers\\winepulse.drv\\devices";
76 static CRITICAL_SECTION session_cs;
77 static CRITICAL_SECTION_DEBUG session_cs_debug = {
78 0, 0, &session_cs,
79 { &session_cs_debug.ProcessLocksList,
80 &session_cs_debug.ProcessLocksList },
81 0, 0, { (DWORD_PTR)(__FILE__ ": session_cs") }
83 static CRITICAL_SECTION session_cs = { &session_cs_debug, -1, 0, 0, 0, 0 };
85 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
87 if (reason == DLL_PROCESS_ATTACH) {
88 DisableThreadLibraryCalls(dll);
89 if (__wine_init_unix_call())
90 return FALSE;
91 } else if (reason == DLL_PROCESS_DETACH) {
92 struct device_cache *device, *device_next;
94 LIST_FOR_EACH_ENTRY_SAFE(device, device_next, &g_devices_cache, struct device_cache, entry)
95 free(device);
97 if (pulse_thread) {
98 WaitForSingleObject(pulse_thread, INFINITE);
99 CloseHandle(pulse_thread);
102 return TRUE;
105 typedef struct ACImpl ACImpl;
107 typedef struct _AudioSession {
108 GUID guid;
109 struct list clients;
111 IMMDevice *device;
113 float master_vol;
114 UINT32 channel_count;
115 float *channel_vols;
116 BOOL mute;
118 struct list entry;
119 } AudioSession;
121 typedef struct _AudioSessionWrapper {
122 IAudioSessionControl2 IAudioSessionControl2_iface;
123 IChannelAudioVolume IChannelAudioVolume_iface;
124 ISimpleAudioVolume ISimpleAudioVolume_iface;
126 LONG ref;
128 ACImpl *client;
129 AudioSession *session;
130 } AudioSessionWrapper;
132 struct ACImpl {
133 IAudioClient3 IAudioClient3_iface;
134 IAudioRenderClient IAudioRenderClient_iface;
135 IAudioCaptureClient IAudioCaptureClient_iface;
136 IAudioClock IAudioClock_iface;
137 IAudioClock2 IAudioClock2_iface;
138 IAudioStreamVolume IAudioStreamVolume_iface;
139 IUnknown *marshal;
140 IMMDevice *parent;
141 struct list entry;
142 float *vol;
144 LONG ref;
145 EDataFlow dataflow;
146 UINT32 channel_count;
147 HANDLE timer;
149 stream_handle pulse_stream;
151 AudioSession *session;
152 AudioSessionWrapper *session_wrapper;
154 char pulse_name[0];
157 static const IAudioClient3Vtbl AudioClient3_Vtbl;
158 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
159 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
160 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
161 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
162 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
163 static const IAudioClockVtbl AudioClock_Vtbl;
164 static const IAudioClock2Vtbl AudioClock2_Vtbl;
165 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
167 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
169 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
171 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
174 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
176 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
179 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
181 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
184 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
186 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
189 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
191 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
194 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
196 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
199 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
201 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
204 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
206 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
209 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
214 static void pulse_call(enum unix_funcs code, void *params)
216 NTSTATUS status;
217 status = WINE_UNIX_CALL(code, params);
218 assert(!status);
221 static void pulse_release_stream(stream_handle stream, HANDLE timer)
223 struct release_stream_params params;
224 params.stream = stream;
225 params.timer_thread = timer;
226 pulse_call(release_stream, &params);
229 static DWORD CALLBACK pulse_mainloop_thread(void *event)
231 struct main_loop_params params;
232 params.event = event;
233 SetThreadDescription(GetCurrentThread(), L"winepulse_mainloop");
234 pulse_call(main_loop, &params);
235 return 0;
238 typedef struct tagLANGANDCODEPAGE
240 WORD wLanguage;
241 WORD wCodePage;
242 } LANGANDCODEPAGE;
244 static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, UINT *len)
246 WCHAR pn[37];
247 swprintf(pn, ARRAY_SIZE(pn), L"\\StringFileInfo\\%04x%04x\\ProductName", lang->wLanguage, lang->wCodePage);
248 return VerQueryValueW(data, pn, buffer, len) && *len;
251 static WCHAR *get_application_name(BOOL query_app_name)
253 WCHAR path[MAX_PATH], *name;
255 GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
257 if (query_app_name)
259 UINT translate_size, productname_size;
260 LANGANDCODEPAGE *translate;
261 LPVOID productname;
262 BOOL found = FALSE;
263 void *data = NULL;
264 unsigned int i;
265 LCID locale;
266 DWORD size;
268 size = GetFileVersionInfoSizeW(path, NULL);
269 if (!size)
270 goto skip;
272 data = malloc(size);
273 if (!data)
274 goto skip;
276 if (!GetFileVersionInfoW(path, 0, size, data))
277 goto skip;
279 if (!VerQueryValueW(data, L"\\VarFileInfo\\Translation", (LPVOID *)&translate, &translate_size))
280 goto skip;
282 /* no translations found */
283 if (translate_size < sizeof(LANGANDCODEPAGE))
284 goto skip;
286 /* The following code will try to find the best translation. We first search for an
287 * exact match of the language, then a match of the language PRIMARYLANGID, then we
288 * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the
289 * first entry which contains a proper productname. */
290 locale = GetThreadLocale();
292 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
293 if (translate[i].wLanguage == locale &&
294 query_productname(data, &translate[i], &productname, &productname_size)) {
295 found = TRUE;
296 break;
300 if (!found) {
301 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
302 if (PRIMARYLANGID(translate[i].wLanguage) == PRIMARYLANGID(locale) &&
303 query_productname(data, &translate[i], &productname, &productname_size)) {
304 found = TRUE;
305 break;
310 if (!found) {
311 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
312 if (PRIMARYLANGID(translate[i].wLanguage) == LANG_NEUTRAL &&
313 query_productname(data, &translate[i], &productname, &productname_size)) {
314 found = TRUE;
315 break;
320 if (!found) {
321 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
322 if (query_productname(data, &translate[i], &productname, &productname_size)) {
323 found = TRUE;
324 break;
329 skip:
330 free(data);
331 if (found) return wcsdup(productname);
334 name = wcsrchr(path, '\\');
335 if (!name)
336 name = path;
337 else
338 name++;
339 return wcsdup(name);
342 static DWORD WINAPI pulse_timer_cb(void *user)
344 struct timer_loop_params params;
345 ACImpl *This = user;
346 params.stream = This->pulse_stream;
347 SetThreadDescription(GetCurrentThread(), L"winepulse_timer_loop");
348 pulse_call(timer_loop, &params);
349 return 0;
352 static void set_stream_volumes(ACImpl *This)
354 struct set_volumes_params params;
355 params.stream = This->pulse_stream;
356 params.master_volume = This->session->mute ? 0.0f : This->session->master_vol;
357 params.volumes = This->vol;
358 params.session_volumes = This->session->channel_vols;
359 params.channel = 0;
360 pulse_call(set_volumes, &params);
363 static void get_device_guid(HKEY drv_key, EDataFlow flow, const char *pulse_name, GUID *guid)
365 WCHAR key_name[MAX_PULSE_NAME_LEN + 2];
366 DWORD type, size = sizeof(*guid);
367 LSTATUS status;
368 HKEY dev_key;
370 if (!pulse_name[0]) {
371 *guid = (flow == eRender) ? pulse_render_guid : pulse_capture_guid;
372 return;
375 if (!drv_key) {
376 CoCreateGuid(guid);
377 return;
380 key_name[0] = (flow == eRender) ? '0' : '1';
381 key_name[1] = ',';
382 MultiByteToWideChar(CP_UNIXCP, 0, pulse_name, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
384 status = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY,
385 NULL, &dev_key, NULL);
386 if (status != ERROR_SUCCESS) {
387 ERR("Failed to open registry key for device %s: %lu\n", pulse_name, status);
388 CoCreateGuid(guid);
389 return;
392 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE*)guid, &size);
393 if (status != ERROR_SUCCESS || type != REG_BINARY || size != sizeof(*guid)) {
394 CoCreateGuid(guid);
395 status = RegSetValueExW(dev_key, L"guid", 0, REG_BINARY, (BYTE*)guid, sizeof(*guid));
396 if (status != ERROR_SUCCESS)
397 ERR("Failed to store device GUID for %s to registry: %lu\n", pulse_name, status);
399 RegCloseKey(dev_key);
402 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **keys,
403 UINT *num, UINT *def_index)
405 struct get_endpoint_ids_params params;
406 GUID *guids = NULL;
407 WCHAR **ids = NULL;
408 unsigned int i = 0;
409 LSTATUS status;
410 HKEY drv_key;
412 TRACE("%d %p %p %p\n", flow, ids_out, num, def_index);
414 params.flow = flow;
415 params.size = MAX_PULSE_NAME_LEN * 4;
416 params.endpoints = NULL;
417 do {
418 HeapFree(GetProcessHeap(), 0, params.endpoints);
419 params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
420 pulse_call(get_endpoint_ids, &params);
421 } while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
423 if (FAILED(params.result))
424 goto end;
426 ids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*ids));
427 guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids));
428 if (!ids || !guids) {
429 params.result = E_OUTOFMEMORY;
430 goto end;
433 status = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0,
434 KEY_WRITE | KEY_WOW64_64KEY, NULL, &drv_key, NULL);
435 if (status != ERROR_SUCCESS) {
436 ERR("Failed to open devices registry key: %lu\n", status);
437 drv_key = NULL;
440 for (i = 0; i < params.num; i++) {
441 WCHAR *name = (WCHAR *)((char *)params.endpoints + params.endpoints[i].name);
442 char *pulse_name = (char *)params.endpoints + params.endpoints[i].device;
443 unsigned int size = (wcslen(name) + 1) * sizeof(WCHAR);
445 if (!(ids[i] = HeapAlloc(GetProcessHeap(), 0, size))) {
446 params.result = E_OUTOFMEMORY;
447 break;
449 memcpy(ids[i], name, size);
450 get_device_guid(drv_key, flow, pulse_name, &guids[i]);
452 if (drv_key)
453 RegCloseKey(drv_key);
455 end:
456 HeapFree(GetProcessHeap(), 0, params.endpoints);
457 if (FAILED(params.result)) {
458 HeapFree(GetProcessHeap(), 0, guids);
459 while (i--) HeapFree(GetProcessHeap(), 0, ids[i]);
460 HeapFree(GetProcessHeap(), 0, ids);
461 } else {
462 *ids_out = ids;
463 *keys = guids;
464 *num = params.num;
465 *def_index = params.default_idx;
467 return params.result;
470 static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_NAME_LEN], EDataFlow *flow)
472 struct device_cache *device;
473 WCHAR key_name[MAX_PULSE_NAME_LEN + 2];
474 DWORD key_name_size;
475 DWORD index = 0;
476 HKEY key;
478 /* Return empty string for default PulseAudio device */
479 pulse_name[0] = 0;
480 if (IsEqualGUID(guid, &pulse_render_guid)) {
481 *flow = eRender;
482 return TRUE;
483 } else if (IsEqualGUID(guid, &pulse_capture_guid)) {
484 *flow = eCapture;
485 return TRUE;
488 /* Check the cache first */
489 LIST_FOR_EACH_ENTRY(device, &g_devices_cache, struct device_cache, entry) {
490 if (!IsEqualGUID(guid, &device->guid))
491 continue;
492 *flow = device->dataflow;
493 strcpy(pulse_name, device->pulse_name);
494 return TRUE;
497 if (RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ | KEY_WOW64_64KEY, &key) != ERROR_SUCCESS) {
498 WARN("No devices found in registry\n");
499 return FALSE;
502 for (;;) {
503 DWORD size, type;
504 LSTATUS status;
505 GUID reg_guid;
506 HKEY dev_key;
507 int len;
509 key_name_size = ARRAY_SIZE(key_name);
510 if (RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
511 break;
513 if (RegOpenKeyExW(key, key_name, 0, KEY_READ | KEY_WOW64_64KEY, &dev_key) != ERROR_SUCCESS) {
514 ERR("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
515 continue;
518 size = sizeof(reg_guid);
519 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE *)&reg_guid, &size);
520 RegCloseKey(dev_key);
522 if (status == ERROR_SUCCESS && type == REG_BINARY && size == sizeof(reg_guid) && IsEqualGUID(&reg_guid, guid)) {
523 RegCloseKey(key);
525 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
527 if (key_name[0] == '0')
528 *flow = eRender;
529 else if (key_name[0] == '1')
530 *flow = eCapture;
531 else {
532 WARN("Unknown device type: %c\n", key_name[0]);
533 return FALSE;
536 if (!(len = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, pulse_name, MAX_PULSE_NAME_LEN, NULL, NULL)))
537 return FALSE;
539 if ((device = malloc(FIELD_OFFSET(struct device_cache, pulse_name[len])))) {
540 device->guid = reg_guid;
541 device->dataflow = *flow;
542 strcpy(device->pulse_name, pulse_name);
543 list_add_tail(&g_devices_cache, &device->entry);
545 return TRUE;
549 RegCloseKey(key);
550 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
551 return FALSE;
554 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
556 ACImpl *This;
557 char pulse_name[MAX_PULSE_NAME_LEN];
558 EDataFlow dataflow;
559 unsigned len;
560 HRESULT hr;
562 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
564 if (!get_pulse_name_by_guid(guid, pulse_name, &dataflow))
565 return AUDCLNT_E_DEVICE_INVALIDATED;
567 *out = NULL;
569 len = strlen(pulse_name) + 1;
570 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(ACImpl, pulse_name[len]));
571 if (!This)
572 return E_OUTOFMEMORY;
574 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
575 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
576 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
577 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
578 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
579 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
580 This->dataflow = dataflow;
581 This->parent = dev;
582 memcpy(This->pulse_name, pulse_name, len);
584 hr = CoCreateFreeThreadedMarshaler((IUnknown*)&This->IAudioClient3_iface, &This->marshal);
585 if (FAILED(hr)) {
586 HeapFree(GetProcessHeap(), 0, This);
587 return hr;
589 IMMDevice_AddRef(This->parent);
591 *out = (IAudioClient *)&This->IAudioClient3_iface;
592 IAudioClient3_AddRef(&This->IAudioClient3_iface);
594 return S_OK;
597 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
598 REFIID riid, void **ppv)
600 ACImpl *This = impl_from_IAudioClient3(iface);
602 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
604 if (!ppv)
605 return E_POINTER;
607 *ppv = NULL;
608 if (IsEqualIID(riid, &IID_IUnknown) ||
609 IsEqualIID(riid, &IID_IAudioClient) ||
610 IsEqualIID(riid, &IID_IAudioClient2) ||
611 IsEqualIID(riid, &IID_IAudioClient3))
612 *ppv = iface;
613 if (*ppv) {
614 IUnknown_AddRef((IUnknown*)*ppv);
615 return S_OK;
618 if (IsEqualIID(riid, &IID_IMarshal))
619 return IUnknown_QueryInterface(This->marshal, riid, ppv);
621 WARN("Unknown interface %s\n", debugstr_guid(riid));
622 return E_NOINTERFACE;
625 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
627 ACImpl *This = impl_from_IAudioClient3(iface);
628 ULONG ref;
629 ref = InterlockedIncrement(&This->ref);
630 TRACE("(%p) Refcount now %lu\n", This, ref);
631 return ref;
634 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
636 ACImpl *This = impl_from_IAudioClient3(iface);
637 ULONG ref;
638 ref = InterlockedDecrement(&This->ref);
639 TRACE("(%p) Refcount now %lu\n", This, ref);
640 if (!ref) {
641 if (This->pulse_stream) {
642 pulse_release_stream(This->pulse_stream, This->timer);
643 This->pulse_stream = 0;
644 EnterCriticalSection(&session_cs);
645 list_remove(&This->entry);
646 LeaveCriticalSection(&session_cs);
648 IUnknown_Release(This->marshal);
649 IMMDevice_Release(This->parent);
650 HeapFree(GetProcessHeap(), 0, This);
652 return ref;
655 static void dump_fmt(const WAVEFORMATEX *fmt)
657 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
658 switch(fmt->wFormatTag) {
659 case WAVE_FORMAT_PCM:
660 TRACE("WAVE_FORMAT_PCM");
661 break;
662 case WAVE_FORMAT_IEEE_FLOAT:
663 TRACE("WAVE_FORMAT_IEEE_FLOAT");
664 break;
665 case WAVE_FORMAT_EXTENSIBLE:
666 TRACE("WAVE_FORMAT_EXTENSIBLE");
667 break;
668 default:
669 TRACE("Unknown");
670 break;
672 TRACE(")\n");
674 TRACE("nChannels: %u\n", fmt->nChannels);
675 TRACE("nSamplesPerSec: %lu\n", fmt->nSamplesPerSec);
676 TRACE("nAvgBytesPerSec: %lu\n", fmt->nAvgBytesPerSec);
677 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
678 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
679 TRACE("cbSize: %u\n", fmt->cbSize);
681 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
682 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
683 TRACE("dwChannelMask: %08lx\n", fmtex->dwChannelMask);
684 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
685 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
689 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
691 WAVEFORMATEX *ret;
692 size_t size;
694 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
695 size = sizeof(WAVEFORMATEXTENSIBLE);
696 else
697 size = sizeof(WAVEFORMATEX);
699 ret = CoTaskMemAlloc(size);
700 if (!ret)
701 return NULL;
703 memcpy(ret, fmt, size);
705 ret->cbSize = size - sizeof(WAVEFORMATEX);
707 return ret;
710 static void session_init_vols(AudioSession *session, UINT channels)
712 if (session->channel_count < channels) {
713 UINT i;
715 if (session->channel_vols)
716 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
717 session->channel_vols, sizeof(float) * channels);
718 else
719 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
720 sizeof(float) * channels);
721 if (!session->channel_vols)
722 return;
724 for(i = session->channel_count; i < channels; ++i)
725 session->channel_vols[i] = 1.f;
727 session->channel_count = channels;
731 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
732 UINT num_channels)
734 AudioSession *ret;
736 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
737 if (!ret)
738 return NULL;
740 memcpy(&ret->guid, guid, sizeof(GUID));
742 ret->device = device;
744 list_init(&ret->clients);
746 list_add_head(&g_sessions, &ret->entry);
748 session_init_vols(ret, num_channels);
750 ret->master_vol = 1.f;
752 return ret;
755 /* if channels == 0, then this will return or create a session with
756 * matching dataflow and GUID. otherwise, channels must also match */
757 static HRESULT get_audio_session(const GUID *sessionguid,
758 IMMDevice *device, UINT channels, AudioSession **out)
760 AudioSession *session;
762 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
763 *out = create_session(&GUID_NULL, device, channels);
764 if (!*out)
765 return E_OUTOFMEMORY;
767 return S_OK;
770 *out = NULL;
771 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
772 if (session->device == device &&
773 IsEqualGUID(sessionguid, &session->guid)) {
774 session_init_vols(session, channels);
775 *out = session;
776 break;
780 if (!*out) {
781 *out = create_session(sessionguid, device, channels);
782 if (!*out)
783 return E_OUTOFMEMORY;
786 return S_OK;
789 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
790 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
791 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
792 const GUID *sessionguid)
794 ACImpl *This = impl_from_IAudioClient3(iface);
795 struct create_stream_params params;
796 unsigned int i, channel_count;
797 stream_handle stream;
798 WCHAR *name;
799 HRESULT hr;
801 TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags,
802 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
804 if (!fmt)
805 return E_POINTER;
806 dump_fmt(fmt);
808 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
809 return E_INVALIDARG;
810 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
811 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
813 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
814 AUDCLNT_STREAMFLAGS_LOOPBACK |
815 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
816 AUDCLNT_STREAMFLAGS_NOPERSIST |
817 AUDCLNT_STREAMFLAGS_RATEADJUST |
818 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
819 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
820 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
821 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
822 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)) {
823 FIXME("Unknown flags: %08lx\n", flags);
824 return E_INVALIDARG;
827 EnterCriticalSection(&session_cs);
829 if (This->pulse_stream) {
830 LeaveCriticalSection(&session_cs);
831 return AUDCLNT_E_ALREADY_INITIALIZED;
834 if (!pulse_thread)
836 HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL);
837 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, event, 0, NULL)))
839 ERR("Failed to create mainloop thread.\n");
840 LeaveCriticalSection(&session_cs);
841 CloseHandle(event);
842 return E_FAIL;
844 SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
845 WaitForSingleObject(event, INFINITE);
846 CloseHandle(event);
849 params.name = name = get_application_name(TRUE);
850 params.device = This->pulse_name;
851 params.flow = This->dataflow;
852 params.share = mode;
853 params.flags = flags;
854 params.duration = duration;
855 params.period = period;
856 params.fmt = fmt;
857 params.stream = &stream;
858 params.channel_count = &channel_count;
859 pulse_call(create_stream, &params);
860 free(name);
861 if (FAILED(hr = params.result))
863 LeaveCriticalSection(&session_cs);
864 return hr;
867 if (!(This->vol = malloc(channel_count * sizeof(*This->vol))))
869 pulse_release_stream(stream, NULL);
870 LeaveCriticalSection(&session_cs);
871 return E_OUTOFMEMORY;
873 for (i = 0; i < channel_count; i++)
874 This->vol[i] = 1.f;
876 hr = get_audio_session(sessionguid, This->parent, channel_count, &This->session);
877 if (FAILED(hr))
879 free(This->vol);
880 This->vol = NULL;
881 LeaveCriticalSection(&session_cs);
882 pulse_release_stream(stream, NULL);
883 return E_OUTOFMEMORY;
886 This->pulse_stream = stream;
887 This->channel_count = channel_count;
888 list_add_tail(&This->session->clients, &This->entry);
889 set_stream_volumes(This);
891 LeaveCriticalSection(&session_cs);
892 return S_OK;
895 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
896 UINT32 *out)
898 ACImpl *This = impl_from_IAudioClient3(iface);
899 struct get_buffer_size_params params;
901 TRACE("(%p)->(%p)\n", This, out);
903 if (!out)
904 return E_POINTER;
905 if (!This->pulse_stream)
906 return AUDCLNT_E_NOT_INITIALIZED;
908 params.stream = This->pulse_stream;
909 params.frames = out;
910 pulse_call(get_buffer_size, &params);
911 return params.result;
914 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
915 REFERENCE_TIME *latency)
917 ACImpl *This = impl_from_IAudioClient3(iface);
918 struct get_latency_params params;
920 TRACE("(%p)->(%p)\n", This, latency);
922 if (!latency)
923 return E_POINTER;
924 if (!This->pulse_stream)
925 return AUDCLNT_E_NOT_INITIALIZED;
927 params.stream = This->pulse_stream;
928 params.latency = latency;
929 pulse_call(get_latency, &params);
930 return params.result;
933 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
934 UINT32 *out)
936 ACImpl *This = impl_from_IAudioClient3(iface);
937 struct get_current_padding_params params;
939 TRACE("(%p)->(%p)\n", This, out);
941 if (!out)
942 return E_POINTER;
943 if (!This->pulse_stream)
944 return AUDCLNT_E_NOT_INITIALIZED;
946 params.stream = This->pulse_stream;
947 params.padding = out;
948 pulse_call(get_current_padding, &params);
949 return params.result;
952 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
953 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
954 WAVEFORMATEX **out)
956 ACImpl *This = impl_from_IAudioClient3(iface);
957 HRESULT hr = S_OK;
958 WAVEFORMATEX *closest = NULL;
959 BOOL exclusive;
961 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
963 if (!fmt)
964 return E_POINTER;
966 if (out)
967 *out = NULL;
969 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
970 exclusive = 1;
971 out = NULL;
972 } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
973 exclusive = 0;
974 if (!out)
975 return E_POINTER;
976 } else
977 return E_INVALIDARG;
979 if (fmt->nChannels == 0)
980 return AUDCLNT_E_UNSUPPORTED_FORMAT;
982 closest = clone_format(fmt);
983 if (!closest)
984 return E_OUTOFMEMORY;
986 dump_fmt(fmt);
988 switch (fmt->wFormatTag) {
989 case WAVE_FORMAT_EXTENSIBLE: {
990 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
992 if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
993 fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
994 fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
995 ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
996 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
997 hr = E_INVALIDARG;
998 break;
1001 if (exclusive) {
1002 UINT32 mask = 0, i, channels = 0;
1004 if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
1005 for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
1006 if (i & ext->dwChannelMask) {
1007 mask |= i;
1008 channels++;
1012 if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
1013 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1014 break;
1016 } else {
1017 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1018 break;
1022 if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
1023 if (fmt->wBitsPerSample != 32) {
1024 hr = E_INVALIDARG;
1025 break;
1028 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
1029 hr = S_FALSE;
1030 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1032 } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1033 if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
1034 hr = E_INVALIDARG;
1035 break;
1038 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
1039 !(fmt->wBitsPerSample == 32 &&
1040 ext->Samples.wValidBitsPerSample == 24)) {
1041 hr = S_FALSE;
1042 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1043 break;
1045 } else {
1046 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1047 break;
1050 break;
1053 case WAVE_FORMAT_ALAW:
1054 case WAVE_FORMAT_MULAW:
1055 if (fmt->wBitsPerSample != 8) {
1056 hr = E_INVALIDARG;
1057 break;
1059 /* Fall-through */
1060 case WAVE_FORMAT_IEEE_FLOAT:
1061 if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
1062 hr = E_INVALIDARG;
1063 break;
1065 /* Fall-through */
1066 case WAVE_FORMAT_PCM:
1067 if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
1068 (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
1069 hr = E_INVALIDARG;
1070 break;
1073 if (fmt->nChannels > 2) {
1074 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1075 break;
1078 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1079 * ignored, invalid values are happily accepted.
1081 break;
1082 default:
1083 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1084 break;
1087 if (exclusive && hr != S_OK) {
1088 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1089 CoTaskMemFree(closest);
1090 } else if (hr != S_FALSE)
1091 CoTaskMemFree(closest);
1092 else
1093 *out = closest;
1095 /* Winepulse does not currently support exclusive mode, if you know of an
1096 * application that uses it, I will correct this..
1098 if (hr == S_OK && exclusive)
1099 return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1101 TRACE("returning: %08lx %p\n", hr, out ? *out : NULL);
1102 return hr;
1105 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
1106 WAVEFORMATEX **pwfx)
1108 ACImpl *This = impl_from_IAudioClient3(iface);
1109 struct get_mix_format_params params;
1111 TRACE("(%p)->(%p)\n", This, pwfx);
1113 if (!pwfx)
1114 return E_POINTER;
1115 *pwfx = NULL;
1117 params.device = This->pulse_name;
1118 params.flow = This->dataflow;
1119 params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
1120 if (!params.fmt)
1121 return E_OUTOFMEMORY;
1123 pulse_call(get_mix_format, &params);
1125 if (SUCCEEDED(params.result)) {
1126 *pwfx = &params.fmt->Format;
1127 dump_fmt(*pwfx);
1128 } else {
1129 CoTaskMemFree(params.fmt);
1132 return params.result;
1135 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
1136 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1138 struct get_device_period_params params;
1139 ACImpl *This = impl_from_IAudioClient3(iface);
1141 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1143 if (!defperiod && !minperiod)
1144 return E_POINTER;
1146 params.flow = This->dataflow;
1147 params.device = This->pulse_name;
1148 params.def_period = defperiod;
1149 params.min_period = minperiod;
1151 pulse_call(get_device_period, &params);
1153 return params.result;
1156 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
1158 ACImpl *This = impl_from_IAudioClient3(iface);
1159 struct start_params params;
1160 HRESULT hr;
1162 TRACE("(%p)\n", This);
1164 if (!This->pulse_stream)
1165 return AUDCLNT_E_NOT_INITIALIZED;
1167 params.stream = This->pulse_stream;
1168 pulse_call(start, &params);
1169 if (FAILED(hr = params.result))
1170 return hr;
1172 if (!This->timer) {
1173 This->timer = CreateThread(NULL, 0, pulse_timer_cb, This, 0, NULL);
1174 SetThreadPriority(This->timer, THREAD_PRIORITY_TIME_CRITICAL);
1177 return S_OK;
1180 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
1182 ACImpl *This = impl_from_IAudioClient3(iface);
1183 struct stop_params params;
1185 TRACE("(%p)\n", This);
1187 if (!This->pulse_stream)
1188 return AUDCLNT_E_NOT_INITIALIZED;
1190 params.stream = This->pulse_stream;
1191 pulse_call(stop, &params);
1192 return params.result;
1195 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
1197 ACImpl *This = impl_from_IAudioClient3(iface);
1198 struct reset_params params;
1200 TRACE("(%p)\n", This);
1202 if (!This->pulse_stream)
1203 return AUDCLNT_E_NOT_INITIALIZED;
1205 params.stream = This->pulse_stream;
1206 pulse_call(reset, &params);
1207 return params.result;
1210 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
1211 HANDLE event)
1213 ACImpl *This = impl_from_IAudioClient3(iface);
1214 struct set_event_handle_params params;
1216 TRACE("(%p)->(%p)\n", This, event);
1218 if (!event)
1219 return E_INVALIDARG;
1220 if (!This->pulse_stream)
1221 return AUDCLNT_E_NOT_INITIALIZED;
1223 params.stream = This->pulse_stream;
1224 params.event = event;
1225 pulse_call(set_event_handle, &params);
1226 return params.result;
1229 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
1230 void **ppv)
1232 ACImpl *This = impl_from_IAudioClient3(iface);
1234 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1236 if (!ppv)
1237 return E_POINTER;
1238 *ppv = NULL;
1240 if (!This->pulse_stream)
1241 return AUDCLNT_E_NOT_INITIALIZED;
1243 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1244 if (This->dataflow != eRender)
1245 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1246 *ppv = &This->IAudioRenderClient_iface;
1247 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1248 if (This->dataflow != eCapture)
1249 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1250 *ppv = &This->IAudioCaptureClient_iface;
1251 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1252 *ppv = &This->IAudioClock_iface;
1253 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1254 *ppv = &This->IAudioStreamVolume_iface;
1255 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1256 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1257 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1258 if (!This->session_wrapper) {
1259 This->session_wrapper = AudioSessionWrapper_Create(This);
1260 if (!This->session_wrapper)
1261 return E_OUTOFMEMORY;
1263 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1264 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1265 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1266 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1267 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1268 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1271 if (*ppv) {
1272 IUnknown_AddRef((IUnknown*)*ppv);
1273 return S_OK;
1276 FIXME("stub %s\n", debugstr_guid(riid));
1277 return E_NOINTERFACE;
1280 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
1281 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
1283 ACImpl *This = impl_from_IAudioClient3(iface);
1285 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
1287 if(!offload_capable)
1288 return E_INVALIDARG;
1290 *offload_capable = FALSE;
1292 return S_OK;
1295 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
1296 const AudioClientProperties *prop)
1298 ACImpl *This = impl_from_IAudioClient3(iface);
1299 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
1301 TRACE("(%p)->(%p)\n", This, prop);
1303 if(!legacy_prop)
1304 return E_POINTER;
1306 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
1307 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
1308 legacy_prop->bIsOffload,
1309 legacy_prop->eCategory,
1310 prop->Options);
1311 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
1312 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
1313 legacy_prop->bIsOffload,
1314 legacy_prop->eCategory);
1315 }else{
1316 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
1317 return E_INVALIDARG;
1321 if(legacy_prop->bIsOffload)
1322 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
1324 return S_OK;
1327 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
1328 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
1329 REFERENCE_TIME *max_duration)
1331 ACImpl *This = impl_from_IAudioClient3(iface);
1333 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
1335 return E_NOTIMPL;
1338 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
1339 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
1340 UINT32 *min_period_frames, UINT32 *max_period_frames)
1342 ACImpl *This = impl_from_IAudioClient3(iface);
1344 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
1345 min_period_frames, max_period_frames);
1347 return E_NOTIMPL;
1350 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
1351 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
1353 ACImpl *This = impl_from_IAudioClient3(iface);
1355 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1357 return E_NOTIMPL;
1360 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
1361 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
1362 const GUID *session_guid)
1364 ACImpl *This = impl_from_IAudioClient3(iface);
1366 FIXME("(%p)->(0x%lx, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
1368 return E_NOTIMPL;
1371 static const IAudioClient3Vtbl AudioClient3_Vtbl =
1373 AudioClient_QueryInterface,
1374 AudioClient_AddRef,
1375 AudioClient_Release,
1376 AudioClient_Initialize,
1377 AudioClient_GetBufferSize,
1378 AudioClient_GetStreamLatency,
1379 AudioClient_GetCurrentPadding,
1380 AudioClient_IsFormatSupported,
1381 AudioClient_GetMixFormat,
1382 AudioClient_GetDevicePeriod,
1383 AudioClient_Start,
1384 AudioClient_Stop,
1385 AudioClient_Reset,
1386 AudioClient_SetEventHandle,
1387 AudioClient_GetService,
1388 AudioClient_IsOffloadCapable,
1389 AudioClient_SetClientProperties,
1390 AudioClient_GetBufferSizeLimits,
1391 AudioClient_GetSharedModeEnginePeriod,
1392 AudioClient_GetCurrentSharedModeEnginePeriod,
1393 AudioClient_InitializeSharedAudioStream,
1396 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1397 IAudioRenderClient *iface, REFIID riid, void **ppv)
1399 ACImpl *This = impl_from_IAudioRenderClient(iface);
1400 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1402 if (!ppv)
1403 return E_POINTER;
1404 *ppv = NULL;
1406 if (IsEqualIID(riid, &IID_IUnknown) ||
1407 IsEqualIID(riid, &IID_IAudioRenderClient))
1408 *ppv = iface;
1409 if (*ppv) {
1410 IUnknown_AddRef((IUnknown*)*ppv);
1411 return S_OK;
1414 if (IsEqualIID(riid, &IID_IMarshal))
1415 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1417 WARN("Unknown interface %s\n", debugstr_guid(riid));
1418 return E_NOINTERFACE;
1421 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1423 ACImpl *This = impl_from_IAudioRenderClient(iface);
1424 return AudioClient_AddRef(&This->IAudioClient3_iface);
1427 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1429 ACImpl *This = impl_from_IAudioRenderClient(iface);
1430 return AudioClient_Release(&This->IAudioClient3_iface);
1433 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1434 UINT32 frames, BYTE **data)
1436 ACImpl *This = impl_from_IAudioRenderClient(iface);
1437 struct get_render_buffer_params params;
1439 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1441 if (!data)
1442 return E_POINTER;
1443 if (!This->pulse_stream)
1444 return AUDCLNT_E_NOT_INITIALIZED;
1445 *data = NULL;
1447 params.stream = This->pulse_stream;
1448 params.frames = frames;
1449 params.data = data;
1450 pulse_call(get_render_buffer, &params);
1451 return params.result;
1454 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1455 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1457 ACImpl *This = impl_from_IAudioRenderClient(iface);
1458 struct release_render_buffer_params params;
1460 TRACE("(%p)->(%u, %lx)\n", This, written_frames, flags);
1462 if (!This->pulse_stream)
1463 return AUDCLNT_E_NOT_INITIALIZED;
1465 params.stream = This->pulse_stream;
1466 params.written_frames = written_frames;
1467 params.flags = flags;
1468 pulse_call(release_render_buffer, &params);
1469 return params.result;
1472 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1473 AudioRenderClient_QueryInterface,
1474 AudioRenderClient_AddRef,
1475 AudioRenderClient_Release,
1476 AudioRenderClient_GetBuffer,
1477 AudioRenderClient_ReleaseBuffer
1480 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1481 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1483 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1484 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1486 if (!ppv)
1487 return E_POINTER;
1488 *ppv = NULL;
1490 if (IsEqualIID(riid, &IID_IUnknown) ||
1491 IsEqualIID(riid, &IID_IAudioCaptureClient))
1492 *ppv = iface;
1493 if (*ppv) {
1494 IUnknown_AddRef((IUnknown*)*ppv);
1495 return S_OK;
1498 if (IsEqualIID(riid, &IID_IMarshal))
1499 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1501 WARN("Unknown interface %s\n", debugstr_guid(riid));
1502 return E_NOINTERFACE;
1505 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1507 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1508 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1511 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1513 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1514 return IAudioClient3_Release(&This->IAudioClient3_iface);
1517 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1518 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1519 UINT64 *qpcpos)
1521 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1522 struct get_capture_buffer_params params;
1524 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1525 devpos, qpcpos);
1527 if (!data)
1528 return E_POINTER;
1529 *data = NULL;
1530 if (!frames || !flags)
1531 return E_POINTER;
1532 if (!This->pulse_stream)
1533 return AUDCLNT_E_NOT_INITIALIZED;
1535 params.stream = This->pulse_stream;
1536 params.data = data;
1537 params.frames = frames;
1538 params.flags = (UINT*)flags;
1539 params.devpos = devpos;
1540 params.qpcpos = qpcpos;
1541 pulse_call(get_capture_buffer, &params);
1542 return params.result;
1545 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1546 IAudioCaptureClient *iface, UINT32 done)
1548 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1549 struct release_capture_buffer_params params;
1551 TRACE("(%p)->(%u)\n", This, done);
1553 if (!This->pulse_stream)
1554 return AUDCLNT_E_NOT_INITIALIZED;
1556 params.stream = This->pulse_stream;
1557 params.done = done;
1558 pulse_call(release_capture_buffer, &params);
1559 return params.result;
1562 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1563 IAudioCaptureClient *iface, UINT32 *frames)
1565 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1566 struct get_next_packet_size_params params;
1568 TRACE("(%p)->(%p)\n", This, frames);
1570 if (!frames)
1571 return E_POINTER;
1572 if (!This->pulse_stream)
1573 return AUDCLNT_E_NOT_INITIALIZED;
1575 params.stream = This->pulse_stream;
1576 params.frames = frames;
1577 pulse_call(get_next_packet_size, &params);
1578 return params.result;
1581 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1583 AudioCaptureClient_QueryInterface,
1584 AudioCaptureClient_AddRef,
1585 AudioCaptureClient_Release,
1586 AudioCaptureClient_GetBuffer,
1587 AudioCaptureClient_ReleaseBuffer,
1588 AudioCaptureClient_GetNextPacketSize
1591 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1592 REFIID riid, void **ppv)
1594 ACImpl *This = impl_from_IAudioClock(iface);
1596 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1598 if (!ppv)
1599 return E_POINTER;
1600 *ppv = NULL;
1602 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1603 *ppv = iface;
1604 else if (IsEqualIID(riid, &IID_IAudioClock2))
1605 *ppv = &This->IAudioClock2_iface;
1606 if (*ppv) {
1607 IUnknown_AddRef((IUnknown*)*ppv);
1608 return S_OK;
1611 if (IsEqualIID(riid, &IID_IMarshal))
1612 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1614 WARN("Unknown interface %s\n", debugstr_guid(riid));
1615 return E_NOINTERFACE;
1618 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1620 ACImpl *This = impl_from_IAudioClock(iface);
1621 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1624 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1626 ACImpl *This = impl_from_IAudioClock(iface);
1627 return IAudioClient3_Release(&This->IAudioClient3_iface);
1630 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1632 ACImpl *This = impl_from_IAudioClock(iface);
1633 struct get_frequency_params params;
1635 TRACE("(%p)->(%p)\n", This, freq);
1637 if (!This->pulse_stream)
1638 return AUDCLNT_E_NOT_INITIALIZED;
1640 params.stream = This->pulse_stream;
1641 params.freq = freq;
1642 pulse_call(get_frequency, &params);
1643 return params.result;
1646 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1647 UINT64 *qpctime)
1649 ACImpl *This = impl_from_IAudioClock(iface);
1650 struct get_position_params params;
1652 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1654 if (!pos)
1655 return E_POINTER;
1656 if (!This->pulse_stream)
1657 return AUDCLNT_E_NOT_INITIALIZED;
1659 params.stream = This->pulse_stream;
1660 params.device = FALSE;
1661 params.pos = pos;
1662 params.qpctime = qpctime;
1663 pulse_call(get_position, &params);
1664 return params.result;
1667 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
1668 DWORD *chars)
1670 ACImpl *This = impl_from_IAudioClock(iface);
1672 TRACE("(%p)->(%p)\n", This, chars);
1674 if (!chars)
1675 return E_POINTER;
1677 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
1679 return S_OK;
1682 static const IAudioClockVtbl AudioClock_Vtbl =
1684 AudioClock_QueryInterface,
1685 AudioClock_AddRef,
1686 AudioClock_Release,
1687 AudioClock_GetFrequency,
1688 AudioClock_GetPosition,
1689 AudioClock_GetCharacteristics
1692 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
1693 REFIID riid, void **ppv)
1695 ACImpl *This = impl_from_IAudioClock2(iface);
1696 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
1699 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
1701 ACImpl *This = impl_from_IAudioClock2(iface);
1702 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1705 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
1707 ACImpl *This = impl_from_IAudioClock2(iface);
1708 return IAudioClient3_Release(&This->IAudioClient3_iface);
1711 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
1712 UINT64 *pos, UINT64 *qpctime)
1714 ACImpl *This = impl_from_IAudioClock2(iface);
1715 struct get_position_params params;
1717 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1719 if (!pos)
1720 return E_POINTER;
1721 if (!This->pulse_stream)
1722 return AUDCLNT_E_NOT_INITIALIZED;
1724 params.stream = This->pulse_stream;
1725 params.device = TRUE;
1726 params.pos = pos;
1727 params.qpctime = qpctime;
1728 pulse_call(get_position, &params);
1729 return params.result;
1732 static const IAudioClock2Vtbl AudioClock2_Vtbl =
1734 AudioClock2_QueryInterface,
1735 AudioClock2_AddRef,
1736 AudioClock2_Release,
1737 AudioClock2_GetDevicePosition
1740 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
1741 IAudioStreamVolume *iface, REFIID riid, void **ppv)
1743 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1745 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1747 if (!ppv)
1748 return E_POINTER;
1749 *ppv = NULL;
1751 if (IsEqualIID(riid, &IID_IUnknown) ||
1752 IsEqualIID(riid, &IID_IAudioStreamVolume))
1753 *ppv = iface;
1754 if (*ppv) {
1755 IUnknown_AddRef((IUnknown*)*ppv);
1756 return S_OK;
1759 if (IsEqualIID(riid, &IID_IMarshal))
1760 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1762 WARN("Unknown interface %s\n", debugstr_guid(riid));
1763 return E_NOINTERFACE;
1766 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
1768 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1769 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1772 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
1774 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1775 return IAudioClient3_Release(&This->IAudioClient3_iface);
1778 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
1779 IAudioStreamVolume *iface, UINT32 *out)
1781 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1783 TRACE("(%p)->(%p)\n", This, out);
1785 if (!out)
1786 return E_POINTER;
1788 *out = This->channel_count;
1790 return S_OK;
1793 struct pulse_info_cb_data {
1794 UINT32 n;
1795 float *levels;
1798 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
1799 IAudioStreamVolume *iface, UINT32 count, const float *levels)
1801 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1802 int i;
1804 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1806 if (!levels)
1807 return E_POINTER;
1809 if (!This->pulse_stream)
1810 return AUDCLNT_E_NOT_INITIALIZED;
1811 if (count != This->channel_count)
1812 return E_INVALIDARG;
1814 EnterCriticalSection(&session_cs);
1815 for (i = 0; i < count; ++i)
1816 This->vol[i] = levels[i];
1818 set_stream_volumes(This);
1819 LeaveCriticalSection(&session_cs);
1820 return S_OK;
1823 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
1824 IAudioStreamVolume *iface, UINT32 count, float *levels)
1826 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1827 int i;
1829 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1831 if (!levels)
1832 return E_POINTER;
1834 if (!This->pulse_stream)
1835 return AUDCLNT_E_NOT_INITIALIZED;
1836 if (count != This->channel_count)
1837 return E_INVALIDARG;
1839 EnterCriticalSection(&session_cs);
1840 for (i = 0; i < count; ++i)
1841 levels[i] = This->vol[i];
1842 LeaveCriticalSection(&session_cs);
1843 return S_OK;
1846 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
1847 IAudioStreamVolume *iface, UINT32 index, float level)
1849 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1851 TRACE("(%p)->(%d, %f)\n", This, index, level);
1853 if (level < 0.f || level > 1.f)
1854 return E_INVALIDARG;
1856 if (!This->pulse_stream)
1857 return AUDCLNT_E_NOT_INITIALIZED;
1858 if (index >= This->channel_count)
1859 return E_INVALIDARG;
1861 EnterCriticalSection(&session_cs);
1862 This->vol[index] = level;
1863 set_stream_volumes(This);
1864 LeaveCriticalSection(&session_cs);
1865 return S_OK;
1868 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
1869 IAudioStreamVolume *iface, UINT32 index, float *level)
1871 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1873 TRACE("(%p)->(%d, %p)\n", This, index, level);
1875 if (!level)
1876 return E_POINTER;
1878 if (!This->pulse_stream)
1879 return AUDCLNT_E_NOT_INITIALIZED;
1880 if (index >= This->channel_count)
1881 return E_INVALIDARG;
1883 EnterCriticalSection(&session_cs);
1884 *level = This->vol[index];
1885 LeaveCriticalSection(&session_cs);
1886 return S_OK;
1889 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
1891 AudioStreamVolume_QueryInterface,
1892 AudioStreamVolume_AddRef,
1893 AudioStreamVolume_Release,
1894 AudioStreamVolume_GetChannelCount,
1895 AudioStreamVolume_SetChannelVolume,
1896 AudioStreamVolume_GetChannelVolume,
1897 AudioStreamVolume_SetAllVolumes,
1898 AudioStreamVolume_GetAllVolumes
1901 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1903 AudioSessionWrapper *ret;
1905 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1906 sizeof(AudioSessionWrapper));
1907 if (!ret)
1908 return NULL;
1910 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1911 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
1912 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
1914 ret->ref = !client;
1916 ret->client = client;
1917 if (client) {
1918 ret->session = client->session;
1919 AudioClient_AddRef(&client->IAudioClient3_iface);
1922 return ret;
1925 static HRESULT WINAPI AudioSessionControl_QueryInterface(
1926 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
1928 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1930 if (!ppv)
1931 return E_POINTER;
1932 *ppv = NULL;
1934 if (IsEqualIID(riid, &IID_IUnknown) ||
1935 IsEqualIID(riid, &IID_IAudioSessionControl) ||
1936 IsEqualIID(riid, &IID_IAudioSessionControl2))
1937 *ppv = iface;
1938 if (*ppv) {
1939 IUnknown_AddRef((IUnknown*)*ppv);
1940 return S_OK;
1943 WARN("Unknown interface %s\n", debugstr_guid(riid));
1944 return E_NOINTERFACE;
1947 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
1949 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1950 ULONG ref;
1951 ref = InterlockedIncrement(&This->ref);
1952 TRACE("(%p) Refcount now %lu\n", This, ref);
1953 return ref;
1956 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
1958 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1959 ULONG ref;
1960 ref = InterlockedDecrement(&This->ref);
1961 TRACE("(%p) Refcount now %lu\n", This, ref);
1962 if (!ref) {
1963 if (This->client) {
1964 This->client->session_wrapper = NULL;
1965 AudioClient_Release(&This->client->IAudioClient3_iface);
1967 HeapFree(GetProcessHeap(), 0, This);
1969 return ref;
1972 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
1973 AudioSessionState *state)
1975 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1976 ACImpl *client;
1978 TRACE("(%p)->(%p)\n", This, state);
1980 if (!state)
1981 return NULL_PTR_ERR;
1983 EnterCriticalSection(&session_cs);
1984 if (list_empty(&This->session->clients)) {
1985 *state = AudioSessionStateExpired;
1986 goto out;
1988 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
1989 struct is_started_params params;
1991 if (!client->pulse_stream)
1992 continue;
1994 params.stream = client->pulse_stream;
1995 pulse_call(is_started, &params);
1996 if (params.result == S_OK) {
1997 *state = AudioSessionStateActive;
1998 goto out;
2001 *state = AudioSessionStateInactive;
2003 out:
2004 LeaveCriticalSection(&session_cs);
2005 return S_OK;
2008 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2009 IAudioSessionControl2 *iface, WCHAR **name)
2011 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2013 FIXME("(%p)->(%p) - stub\n", This, name);
2015 return E_NOTIMPL;
2018 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2019 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2021 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2023 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2025 return E_NOTIMPL;
2028 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2029 IAudioSessionControl2 *iface, WCHAR **path)
2031 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2033 FIXME("(%p)->(%p) - stub\n", This, path);
2035 return E_NOTIMPL;
2038 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2039 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2041 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2043 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2045 return E_NOTIMPL;
2048 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2049 IAudioSessionControl2 *iface, GUID *group)
2051 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2053 FIXME("(%p)->(%p) - stub\n", This, group);
2055 return E_NOTIMPL;
2058 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2059 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2061 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2063 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2064 debugstr_guid(session));
2066 return E_NOTIMPL;
2069 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2070 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2072 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2074 FIXME("(%p)->(%p) - stub\n", This, events);
2076 return S_OK;
2079 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2080 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2082 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2084 FIXME("(%p)->(%p) - stub\n", This, events);
2086 return S_OK;
2089 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2090 IAudioSessionControl2 *iface, WCHAR **id)
2092 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2094 FIXME("(%p)->(%p) - stub\n", This, id);
2096 return E_NOTIMPL;
2099 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2100 IAudioSessionControl2 *iface, WCHAR **id)
2102 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2104 FIXME("(%p)->(%p) - stub\n", This, id);
2106 return E_NOTIMPL;
2109 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2110 IAudioSessionControl2 *iface, DWORD *pid)
2112 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2114 TRACE("(%p)->(%p)\n", This, pid);
2116 if (!pid)
2117 return E_POINTER;
2119 *pid = GetCurrentProcessId();
2121 return S_OK;
2124 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2125 IAudioSessionControl2 *iface)
2127 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2129 TRACE("(%p)\n", This);
2131 return S_FALSE;
2134 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2135 IAudioSessionControl2 *iface, BOOL optout)
2137 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2139 TRACE("(%p)->(%d)\n", This, optout);
2141 return S_OK;
2144 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2146 AudioSessionControl_QueryInterface,
2147 AudioSessionControl_AddRef,
2148 AudioSessionControl_Release,
2149 AudioSessionControl_GetState,
2150 AudioSessionControl_GetDisplayName,
2151 AudioSessionControl_SetDisplayName,
2152 AudioSessionControl_GetIconPath,
2153 AudioSessionControl_SetIconPath,
2154 AudioSessionControl_GetGroupingParam,
2155 AudioSessionControl_SetGroupingParam,
2156 AudioSessionControl_RegisterAudioSessionNotification,
2157 AudioSessionControl_UnregisterAudioSessionNotification,
2158 AudioSessionControl_GetSessionIdentifier,
2159 AudioSessionControl_GetSessionInstanceIdentifier,
2160 AudioSessionControl_GetProcessId,
2161 AudioSessionControl_IsSystemSoundsSession,
2162 AudioSessionControl_SetDuckingPreference
2165 typedef struct _SessionMgr {
2166 IAudioSessionManager2 IAudioSessionManager2_iface;
2168 LONG ref;
2170 IMMDevice *device;
2171 } SessionMgr;
2173 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2174 REFIID riid, void **ppv)
2176 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2178 if (!ppv)
2179 return E_POINTER;
2180 *ppv = NULL;
2182 if (IsEqualIID(riid, &IID_IUnknown) ||
2183 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2184 IsEqualIID(riid, &IID_IAudioSessionManager2))
2185 *ppv = iface;
2186 if (*ppv) {
2187 IUnknown_AddRef((IUnknown*)*ppv);
2188 return S_OK;
2191 WARN("Unknown interface %s\n", debugstr_guid(riid));
2192 return E_NOINTERFACE;
2195 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
2197 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
2200 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2202 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2203 ULONG ref;
2204 ref = InterlockedIncrement(&This->ref);
2205 TRACE("(%p) Refcount now %lu\n", This, ref);
2206 return ref;
2209 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2211 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2212 ULONG ref;
2213 ref = InterlockedDecrement(&This->ref);
2214 TRACE("(%p) Refcount now %lu\n", This, ref);
2215 if (!ref)
2216 HeapFree(GetProcessHeap(), 0, This);
2217 return ref;
2220 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2221 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2222 IAudioSessionControl **out)
2224 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2225 AudioSession *session;
2226 AudioSessionWrapper *wrapper;
2227 HRESULT hr;
2229 TRACE("(%p)->(%s, %lx, %p)\n", This, debugstr_guid(session_guid),
2230 flags, out);
2232 hr = get_audio_session(session_guid, This->device, 0, &session);
2233 if (FAILED(hr))
2234 return hr;
2236 wrapper = AudioSessionWrapper_Create(NULL);
2237 if (!wrapper)
2238 return E_OUTOFMEMORY;
2240 wrapper->session = session;
2242 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2244 return S_OK;
2247 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2248 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2249 ISimpleAudioVolume **out)
2251 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2252 AudioSession *session;
2253 AudioSessionWrapper *wrapper;
2254 HRESULT hr;
2256 TRACE("(%p)->(%s, %lx, %p)\n", This, debugstr_guid(session_guid),
2257 flags, out);
2259 hr = get_audio_session(session_guid, This->device, 0, &session);
2260 if (FAILED(hr))
2261 return hr;
2263 wrapper = AudioSessionWrapper_Create(NULL);
2264 if (!wrapper)
2265 return E_OUTOFMEMORY;
2267 wrapper->session = session;
2269 *out = &wrapper->ISimpleAudioVolume_iface;
2271 return S_OK;
2274 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2275 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2277 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2278 FIXME("(%p)->(%p) - stub\n", This, out);
2279 return E_NOTIMPL;
2282 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2283 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2285 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2286 FIXME("(%p)->(%p) - stub\n", This, notification);
2287 return E_NOTIMPL;
2290 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2291 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2293 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2294 FIXME("(%p)->(%p) - stub\n", This, notification);
2295 return E_NOTIMPL;
2298 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2299 IAudioSessionManager2 *iface, const WCHAR *session_id,
2300 IAudioVolumeDuckNotification *notification)
2302 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2303 FIXME("(%p)->(%p) - stub\n", This, notification);
2304 return E_NOTIMPL;
2307 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2308 IAudioSessionManager2 *iface,
2309 IAudioVolumeDuckNotification *notification)
2311 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2312 FIXME("(%p)->(%p) - stub\n", This, notification);
2313 return E_NOTIMPL;
2316 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2318 AudioSessionManager_QueryInterface,
2319 AudioSessionManager_AddRef,
2320 AudioSessionManager_Release,
2321 AudioSessionManager_GetAudioSessionControl,
2322 AudioSessionManager_GetSimpleAudioVolume,
2323 AudioSessionManager_GetSessionEnumerator,
2324 AudioSessionManager_RegisterSessionNotification,
2325 AudioSessionManager_UnregisterSessionNotification,
2326 AudioSessionManager_RegisterDuckNotification,
2327 AudioSessionManager_UnregisterDuckNotification
2330 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2331 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2333 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2335 if (!ppv)
2336 return E_POINTER;
2337 *ppv = NULL;
2339 if (IsEqualIID(riid, &IID_IUnknown) ||
2340 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2341 *ppv = iface;
2342 if (*ppv) {
2343 IUnknown_AddRef((IUnknown*)*ppv);
2344 return S_OK;
2347 WARN("Unknown interface %s\n", debugstr_guid(riid));
2348 return E_NOINTERFACE;
2351 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2353 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2354 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2357 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2359 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2360 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2363 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2364 ISimpleAudioVolume *iface, float level, const GUID *context)
2366 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2367 AudioSession *session = This->session;
2368 ACImpl *client;
2370 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2372 if (level < 0.f || level > 1.f)
2373 return E_INVALIDARG;
2375 if (context)
2376 FIXME("Notifications not supported yet\n");
2378 TRACE("PulseAudio does not support session volume control\n");
2380 EnterCriticalSection(&session_cs);
2381 session->master_vol = level;
2382 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2383 set_stream_volumes(client);
2384 LeaveCriticalSection(&session_cs);
2386 return S_OK;
2389 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2390 ISimpleAudioVolume *iface, float *level)
2392 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2393 AudioSession *session = This->session;
2395 TRACE("(%p)->(%p)\n", session, level);
2397 if (!level)
2398 return NULL_PTR_ERR;
2400 *level = session->master_vol;
2402 return S_OK;
2405 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2406 BOOL mute, const GUID *context)
2408 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2409 AudioSession *session = This->session;
2410 ACImpl *client;
2412 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2414 if (context)
2415 FIXME("Notifications not supported yet\n");
2417 EnterCriticalSection(&session_cs);
2418 session->mute = mute;
2419 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2420 set_stream_volumes(client);
2421 LeaveCriticalSection(&session_cs);
2423 return S_OK;
2426 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2427 BOOL *mute)
2429 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2430 AudioSession *session = This->session;
2432 TRACE("(%p)->(%p)\n", session, mute);
2434 if (!mute)
2435 return NULL_PTR_ERR;
2437 *mute = session->mute;
2439 return S_OK;
2442 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2444 SimpleAudioVolume_QueryInterface,
2445 SimpleAudioVolume_AddRef,
2446 SimpleAudioVolume_Release,
2447 SimpleAudioVolume_SetMasterVolume,
2448 SimpleAudioVolume_GetMasterVolume,
2449 SimpleAudioVolume_SetMute,
2450 SimpleAudioVolume_GetMute
2453 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2454 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2456 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2458 if (!ppv)
2459 return E_POINTER;
2460 *ppv = NULL;
2462 if (IsEqualIID(riid, &IID_IUnknown) ||
2463 IsEqualIID(riid, &IID_IChannelAudioVolume))
2464 *ppv = iface;
2465 if (*ppv) {
2466 IUnknown_AddRef((IUnknown*)*ppv);
2467 return S_OK;
2470 WARN("Unknown interface %s\n", debugstr_guid(riid));
2471 return E_NOINTERFACE;
2474 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2476 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2477 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2480 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2482 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2483 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2486 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2487 IChannelAudioVolume *iface, UINT32 *out)
2489 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2490 AudioSession *session = This->session;
2492 TRACE("(%p)->(%p)\n", session, out);
2494 if (!out)
2495 return NULL_PTR_ERR;
2497 *out = session->channel_count;
2499 return S_OK;
2502 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2503 IChannelAudioVolume *iface, UINT32 index, float level,
2504 const GUID *context)
2506 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2507 AudioSession *session = This->session;
2508 ACImpl *client;
2510 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2511 wine_dbgstr_guid(context));
2513 if (level < 0.f || level > 1.f)
2514 return E_INVALIDARG;
2516 if (index >= session->channel_count)
2517 return E_INVALIDARG;
2519 if (context)
2520 FIXME("Notifications not supported yet\n");
2522 TRACE("PulseAudio does not support session volume control\n");
2524 EnterCriticalSection(&session_cs);
2525 session->channel_vols[index] = level;
2526 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2527 set_stream_volumes(client);
2528 LeaveCriticalSection(&session_cs);
2530 return S_OK;
2533 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2534 IChannelAudioVolume *iface, UINT32 index, float *level)
2536 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2537 AudioSession *session = This->session;
2539 TRACE("(%p)->(%d, %p)\n", session, index, level);
2541 if (!level)
2542 return NULL_PTR_ERR;
2544 if (index >= session->channel_count)
2545 return E_INVALIDARG;
2547 *level = session->channel_vols[index];
2549 return S_OK;
2552 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2553 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2554 const GUID *context)
2556 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2557 AudioSession *session = This->session;
2558 ACImpl *client;
2559 int i;
2561 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2562 wine_dbgstr_guid(context));
2564 if (!levels)
2565 return NULL_PTR_ERR;
2567 if (count != session->channel_count)
2568 return E_INVALIDARG;
2570 if (context)
2571 FIXME("Notifications not supported yet\n");
2573 TRACE("PulseAudio does not support session volume control\n");
2575 EnterCriticalSection(&session_cs);
2576 for(i = 0; i < count; ++i)
2577 session->channel_vols[i] = levels[i];
2578 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2579 set_stream_volumes(client);
2580 LeaveCriticalSection(&session_cs);
2581 return S_OK;
2584 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2585 IChannelAudioVolume *iface, UINT32 count, float *levels)
2587 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2588 AudioSession *session = This->session;
2589 int i;
2591 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2593 if (!levels)
2594 return NULL_PTR_ERR;
2596 if (count != session->channel_count)
2597 return E_INVALIDARG;
2599 for(i = 0; i < count; ++i)
2600 levels[i] = session->channel_vols[i];
2602 return S_OK;
2605 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2607 ChannelAudioVolume_QueryInterface,
2608 ChannelAudioVolume_AddRef,
2609 ChannelAudioVolume_Release,
2610 ChannelAudioVolume_GetChannelCount,
2611 ChannelAudioVolume_SetChannelVolume,
2612 ChannelAudioVolume_GetChannelVolume,
2613 ChannelAudioVolume_SetAllVolumes,
2614 ChannelAudioVolume_GetAllVolumes
2617 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
2618 IAudioSessionManager2 **out)
2620 SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
2621 *out = NULL;
2622 if (!This)
2623 return E_OUTOFMEMORY;
2624 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
2625 This->device = device;
2626 This->ref = 1;
2627 *out = &This->IAudioSessionManager2_iface;
2628 return S_OK;
2631 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
2633 struct get_prop_value_params params;
2634 char pulse_name[MAX_PULSE_NAME_LEN];
2635 unsigned int size = 0;
2637 TRACE("%s, (%s,%lu), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
2639 if (!get_pulse_name_by_guid(guid, pulse_name, &params.flow))
2640 return E_FAIL;
2642 params.device = pulse_name;
2643 params.guid = guid;
2644 params.prop = prop;
2645 params.value = out;
2646 params.buffer = NULL;
2647 params.buffer_size = &size;
2649 while(1) {
2650 pulse_call(get_prop_value, &params);
2652 if(params.result != E_NOT_SUFFICIENT_BUFFER)
2653 break;
2655 CoTaskMemFree(params.buffer);
2656 params.buffer = CoTaskMemAlloc(*params.buffer_size);
2657 if(!params.buffer)
2658 return E_OUTOFMEMORY;
2660 if(FAILED(params.result))
2661 CoTaskMemFree(params.buffer);
2663 return params.result;