win32u: Move process_sent_messages implementation from user32.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob7b5ef7cfe39cab50285267efa4fa2ab981aabe2e
1 /*
2 * Copyright 2011-2012 Maarten Lankhorst
3 * Copyright 2010-2011 Maarten Lankhorst for CodeWeavers
4 * Copyright 2011 Andrew Eikum for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define COBJMACROS
23 #include <stdarg.h>
24 #include <assert.h>
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winternl.h"
29 #include "wine/debug.h"
30 #include "wine/list.h"
32 #include "ole2.h"
33 #include "mimeole.h"
34 #include "dshow.h"
35 #include "dsound.h"
36 #include "propsys.h"
38 #include "initguid.h"
39 #include "ks.h"
40 #include "ksmedia.h"
41 #include "propkey.h"
42 #include "mmdeviceapi.h"
43 #include "audioclient.h"
44 #include "endpointvolume.h"
45 #include "audiopolicy.h"
47 #include "unixlib.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(pulse);
51 static unixlib_handle_t pulse_handle;
53 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
55 /* From <dlls/mmdevapi/mmdevapi.h> */
56 enum DriverPriority {
57 Priority_Unavailable = 0,
58 Priority_Low,
59 Priority_Neutral,
60 Priority_Preferred
63 static struct pulse_config pulse_config;
65 static HANDLE pulse_thread;
66 static struct list g_sessions = LIST_INIT(g_sessions);
67 static struct list g_devices_cache = LIST_INIT(g_devices_cache);
69 struct device_cache {
70 struct list entry;
71 GUID guid;
72 EDataFlow dataflow;
73 char pulse_name[0];
76 static GUID pulse_render_guid =
77 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
78 static GUID pulse_capture_guid =
79 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
81 static const WCHAR *drv_key_devicesW = L"Software\\Wine\\Drivers\\winepulse.drv\\devices";
83 static CRITICAL_SECTION session_cs;
84 static CRITICAL_SECTION_DEBUG session_cs_debug = {
85 0, 0, &session_cs,
86 { &session_cs_debug.ProcessLocksList,
87 &session_cs_debug.ProcessLocksList },
88 0, 0, { (DWORD_PTR)(__FILE__ ": session_cs") }
90 static CRITICAL_SECTION session_cs = { &session_cs_debug, -1, 0, 0, 0, 0 };
92 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
94 if (reason == DLL_PROCESS_ATTACH) {
95 DisableThreadLibraryCalls(dll);
96 if (NtQueryVirtualMemory( GetCurrentProcess(), dll, MemoryWineUnixFuncs,
97 &pulse_handle, sizeof(pulse_handle), NULL ))
98 return FALSE;
99 if (__wine_unix_call(pulse_handle, process_attach, NULL))
100 return FALSE;
101 } else if (reason == DLL_PROCESS_DETACH) {
102 struct device_cache *device, *device_next;
104 LIST_FOR_EACH_ENTRY_SAFE(device, device_next, &g_devices_cache, struct device_cache, entry)
105 free(device);
106 __wine_unix_call(pulse_handle, process_detach, NULL);
107 if (pulse_thread) {
108 WaitForSingleObject(pulse_thread, INFINITE);
109 CloseHandle(pulse_thread);
112 return TRUE;
115 typedef struct ACImpl ACImpl;
117 typedef struct _AudioSession {
118 GUID guid;
119 struct list clients;
121 IMMDevice *device;
123 float master_vol;
124 UINT32 channel_count;
125 float *channel_vols;
126 BOOL mute;
128 struct list entry;
129 } AudioSession;
131 typedef struct _AudioSessionWrapper {
132 IAudioSessionControl2 IAudioSessionControl2_iface;
133 IChannelAudioVolume IChannelAudioVolume_iface;
134 ISimpleAudioVolume ISimpleAudioVolume_iface;
136 LONG ref;
138 ACImpl *client;
139 AudioSession *session;
140 } AudioSessionWrapper;
142 struct ACImpl {
143 IAudioClient3 IAudioClient3_iface;
144 IAudioRenderClient IAudioRenderClient_iface;
145 IAudioCaptureClient IAudioCaptureClient_iface;
146 IAudioClock IAudioClock_iface;
147 IAudioClock2 IAudioClock2_iface;
148 IAudioStreamVolume IAudioStreamVolume_iface;
149 IUnknown *marshal;
150 IMMDevice *parent;
151 struct list entry;
152 float *vol;
154 LONG ref;
155 EDataFlow dataflow;
156 UINT32 channel_count;
157 HANDLE timer;
159 struct pulse_stream *pulse_stream;
161 AudioSession *session;
162 AudioSessionWrapper *session_wrapper;
164 char pulse_name[0];
167 static const IAudioClient3Vtbl AudioClient3_Vtbl;
168 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
169 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
170 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
171 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
172 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
173 static const IAudioClockVtbl AudioClock_Vtbl;
174 static const IAudioClock2Vtbl AudioClock2_Vtbl;
175 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
177 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
179 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
181 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
184 static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
186 return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
189 static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
191 return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
194 static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface)
196 return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface);
199 static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface)
201 return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface);
204 static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface)
206 return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface);
209 static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface)
211 return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface);
214 static inline ACImpl *impl_from_IAudioClock2(IAudioClock2 *iface)
216 return CONTAINING_RECORD(iface, ACImpl, IAudioClock2_iface);
219 static inline ACImpl *impl_from_IAudioStreamVolume(IAudioStreamVolume *iface)
221 return CONTAINING_RECORD(iface, ACImpl, IAudioStreamVolume_iface);
224 static void pulse_call(enum unix_funcs code, void *params)
226 NTSTATUS status;
227 status = __wine_unix_call(pulse_handle, code, params);
228 assert(!status);
231 static void pulse_release_stream(struct pulse_stream *stream, HANDLE timer)
233 struct release_stream_params params;
234 params.stream = stream;
235 params.timer = timer;
236 pulse_call(release_stream, &params);
239 static DWORD CALLBACK pulse_mainloop_thread(void *event)
241 struct main_loop_params params;
242 params.event = event;
243 pulse_call(main_loop, &params);
244 return 0;
247 typedef struct tagLANGANDCODEPAGE
249 WORD wLanguage;
250 WORD wCodePage;
251 } LANGANDCODEPAGE;
253 static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, DWORD *len)
255 WCHAR pn[37];
256 swprintf(pn, ARRAY_SIZE(pn), L"\\StringFileInfo\\%04x%04x\\ProductName", lang->wLanguage, lang->wCodePage);
257 return VerQueryValueW(data, pn, buffer, len) && *len;
260 static char *get_application_name(BOOL query_app_name)
262 WCHAR path[MAX_PATH], *name;
263 char *str = NULL;
264 size_t len;
266 GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
268 if (query_app_name)
270 UINT translate_size, productname_size;
271 LANGANDCODEPAGE *translate;
272 LPVOID productname;
273 BOOL found = FALSE;
274 void *data = NULL;
275 unsigned int i;
276 LCID locale;
277 DWORD size;
279 size = GetFileVersionInfoSizeW(path, NULL);
280 if (!size)
281 goto skip;
283 data = malloc(size);
284 if (!data)
285 goto skip;
287 if (!GetFileVersionInfoW(path, 0, size, data))
288 goto skip;
290 if (!VerQueryValueW(data, L"\\VarFileInfo\\Translation", (LPVOID *)&translate, &translate_size))
291 goto skip;
293 /* no translations found */
294 if (translate_size < sizeof(LANGANDCODEPAGE))
295 goto skip;
297 /* The following code will try to find the best translation. We first search for an
298 * exact match of the language, then a match of the language PRIMARYLANGID, then we
299 * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the
300 * first entry which contains a proper productname. */
301 locale = GetThreadLocale();
303 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
304 if (translate[i].wLanguage == locale &&
305 query_productname(data, &translate[i], &productname, &productname_size)) {
306 found = TRUE;
307 break;
311 if (!found) {
312 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
313 if (PRIMARYLANGID(translate[i].wLanguage) == PRIMARYLANGID(locale) &&
314 query_productname(data, &translate[i], &productname, &productname_size)) {
315 found = TRUE;
316 break;
321 if (!found) {
322 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
323 if (PRIMARYLANGID(translate[i].wLanguage) == LANG_NEUTRAL &&
324 query_productname(data, &translate[i], &productname, &productname_size)) {
325 found = TRUE;
326 break;
331 if (!found) {
332 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
333 if (query_productname(data, &translate[i], &productname, &productname_size)) {
334 found = TRUE;
335 break;
340 if (found) {
341 len = WideCharToMultiByte(CP_UTF8, 0, productname, -1, NULL, 0, NULL, NULL);
342 str = malloc(len);
343 if (str) WideCharToMultiByte(CP_UTF8, 0, productname, -1, str, len, NULL, NULL);
346 skip:
347 free(data);
348 if (str) return str;
351 name = wcsrchr(path, '\\');
352 if (!name)
353 name = path;
354 else
355 name++;
356 len = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL);
357 if (!(str = malloc(len)))
358 return NULL;
359 WideCharToMultiByte(CP_UNIXCP, 0, name, -1, str, len, NULL, NULL);
360 return str;
363 static DWORD WINAPI pulse_timer_cb(void *user)
365 struct timer_loop_params params;
366 ACImpl *This = user;
367 params.stream = This->pulse_stream;
368 pulse_call(timer_loop, &params);
369 return 0;
372 static void set_stream_volumes(ACImpl *This)
374 struct set_volumes_params params;
375 params.stream = This->pulse_stream;
376 params.master_volume = This->session->mute ? 0.0f : This->session->master_vol;
377 params.volumes = This->vol;
378 params.session_volumes = This->session->channel_vols;
379 pulse_call(set_volumes, &params);
382 static void get_device_guid(HKEY drv_key, EDataFlow flow, const char *pulse_name, GUID *guid)
384 WCHAR key_name[MAX_PULSE_NAME_LEN + 2];
385 DWORD type, size = sizeof(*guid);
386 LSTATUS status;
387 HKEY dev_key;
389 if (!pulse_name[0]) {
390 *guid = (flow == eRender) ? pulse_render_guid : pulse_capture_guid;
391 return;
394 if (!drv_key) {
395 CoCreateGuid(guid);
396 return;
399 key_name[0] = (flow == eRender) ? '0' : '1';
400 key_name[1] = ',';
401 MultiByteToWideChar(CP_UNIXCP, 0, pulse_name, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
403 status = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY,
404 NULL, &dev_key, NULL);
405 if (status != ERROR_SUCCESS) {
406 ERR("Failed to open registry key for device %s: %u\n", pulse_name, status);
407 CoCreateGuid(guid);
408 return;
411 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE*)guid, &size);
412 if (status != ERROR_SUCCESS || type != REG_BINARY || size != sizeof(*guid)) {
413 CoCreateGuid(guid);
414 status = RegSetValueExW(dev_key, L"guid", 0, REG_BINARY, (BYTE*)guid, sizeof(*guid));
415 if (status != ERROR_SUCCESS)
416 ERR("Failed to store device GUID for %s to registry: %u\n", pulse_name, status);
418 RegCloseKey(dev_key);
421 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **keys,
422 UINT *num, UINT *def_index)
424 struct get_endpoint_ids_params params;
425 GUID *guids = NULL;
426 WCHAR **ids = NULL;
427 unsigned int i = 0;
428 LSTATUS status;
429 HKEY drv_key;
431 TRACE("%d %p %p %p\n", flow, ids_out, num, def_index);
433 params.flow = flow;
434 params.size = MAX_PULSE_NAME_LEN * 4;
435 params.endpoints = NULL;
436 do {
437 HeapFree(GetProcessHeap(), 0, params.endpoints);
438 params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
439 pulse_call(get_endpoint_ids, &params);
440 } while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
442 if (FAILED(params.result))
443 goto end;
445 ids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*ids));
446 guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids));
447 if (!ids || !guids) {
448 params.result = E_OUTOFMEMORY;
449 goto end;
452 status = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0,
453 KEY_WRITE | KEY_WOW64_64KEY, NULL, &drv_key, NULL);
454 if (status != ERROR_SUCCESS) {
455 ERR("Failed to open devices registry key: %u\n", status);
456 drv_key = NULL;
459 for (i = 0; i < params.num; i++) {
460 unsigned int size = (wcslen(params.endpoints[i].name) + 1) * sizeof(WCHAR);
461 if (!(ids[i] = HeapAlloc(GetProcessHeap(), 0, size))) {
462 params.result = E_OUTOFMEMORY;
463 break;
465 memcpy(ids[i], params.endpoints[i].name, size);
466 get_device_guid(drv_key, flow, params.endpoints[i].pulse_name, &guids[i]);
468 if (drv_key)
469 RegCloseKey(drv_key);
471 end:
472 HeapFree(GetProcessHeap(), 0, params.endpoints);
473 if (FAILED(params.result)) {
474 HeapFree(GetProcessHeap(), 0, guids);
475 while (i--) HeapFree(GetProcessHeap(), 0, ids[i]);
476 HeapFree(GetProcessHeap(), 0, ids);
477 } else {
478 *ids_out = ids;
479 *keys = guids;
480 *num = params.num;
481 *def_index = params.default_idx;
483 return params.result;
486 int WINAPI AUDDRV_GetPriority(void)
488 struct test_connect_params params;
489 char *name;
491 params.name = name = get_application_name(FALSE);
492 params.config = &pulse_config;
493 pulse_call(test_connect, &params);
494 free(name);
495 return SUCCEEDED(params.result) ? Priority_Preferred : Priority_Unavailable;
498 static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_NAME_LEN], EDataFlow *flow)
500 struct device_cache *device;
501 WCHAR key_name[MAX_PULSE_NAME_LEN + 2];
502 DWORD key_name_size;
503 DWORD index = 0;
504 HKEY key;
506 /* Return empty string for default PulseAudio device */
507 pulse_name[0] = 0;
508 if (IsEqualGUID(guid, &pulse_render_guid)) {
509 *flow = eRender;
510 return TRUE;
511 } else if (IsEqualGUID(guid, &pulse_capture_guid)) {
512 *flow = eCapture;
513 return TRUE;
516 /* Check the cache first */
517 LIST_FOR_EACH_ENTRY(device, &g_devices_cache, struct device_cache, entry) {
518 if (!IsEqualGUID(guid, &device->guid))
519 continue;
520 *flow = device->dataflow;
521 strcpy(pulse_name, device->pulse_name);
522 return TRUE;
525 if (RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ | KEY_WOW64_64KEY, &key) != ERROR_SUCCESS) {
526 WARN("No devices found in registry\n");
527 return FALSE;
530 for (;;) {
531 DWORD size, type;
532 LSTATUS status;
533 GUID reg_guid;
534 HKEY dev_key;
535 int len;
537 key_name_size = ARRAY_SIZE(key_name);
538 if (RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
539 break;
541 if (RegOpenKeyExW(key, key_name, 0, KEY_READ | KEY_WOW64_64KEY, &dev_key) != ERROR_SUCCESS) {
542 ERR("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
543 continue;
546 size = sizeof(reg_guid);
547 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE *)&reg_guid, &size);
548 RegCloseKey(dev_key);
550 if (status == ERROR_SUCCESS && type == REG_BINARY && size == sizeof(reg_guid) && IsEqualGUID(&reg_guid, guid)) {
551 RegCloseKey(key);
553 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
555 if (key_name[0] == '0')
556 *flow = eRender;
557 else if (key_name[0] == '1')
558 *flow = eCapture;
559 else {
560 WARN("Unknown device type: %c\n", key_name[0]);
561 return FALSE;
564 if (!(len = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, pulse_name, MAX_PULSE_NAME_LEN, NULL, NULL)))
565 return FALSE;
567 if ((device = malloc(FIELD_OFFSET(struct device_cache, pulse_name[len])))) {
568 device->guid = reg_guid;
569 device->dataflow = *flow;
570 strcpy(device->pulse_name, pulse_name);
571 list_add_tail(&g_devices_cache, &device->entry);
573 return TRUE;
577 RegCloseKey(key);
578 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
579 return FALSE;
582 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
584 ACImpl *This;
585 char pulse_name[MAX_PULSE_NAME_LEN];
586 EDataFlow dataflow;
587 unsigned len;
588 HRESULT hr;
590 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
592 if (!get_pulse_name_by_guid(guid, pulse_name, &dataflow))
593 return AUDCLNT_E_DEVICE_INVALIDATED;
595 *out = NULL;
597 len = strlen(pulse_name) + 1;
598 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(ACImpl, pulse_name[len]));
599 if (!This)
600 return E_OUTOFMEMORY;
602 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
603 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
604 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
605 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
606 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
607 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
608 This->dataflow = dataflow;
609 This->parent = dev;
610 memcpy(This->pulse_name, pulse_name, len);
612 hr = CoCreateFreeThreadedMarshaler((IUnknown*)&This->IAudioClient3_iface, &This->marshal);
613 if (FAILED(hr)) {
614 HeapFree(GetProcessHeap(), 0, This);
615 return hr;
617 IMMDevice_AddRef(This->parent);
619 *out = (IAudioClient *)&This->IAudioClient3_iface;
620 IAudioClient3_AddRef(&This->IAudioClient3_iface);
622 return S_OK;
625 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
626 REFIID riid, void **ppv)
628 ACImpl *This = impl_from_IAudioClient3(iface);
630 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
632 if (!ppv)
633 return E_POINTER;
635 *ppv = NULL;
636 if (IsEqualIID(riid, &IID_IUnknown) ||
637 IsEqualIID(riid, &IID_IAudioClient) ||
638 IsEqualIID(riid, &IID_IAudioClient2) ||
639 IsEqualIID(riid, &IID_IAudioClient3))
640 *ppv = iface;
641 if (*ppv) {
642 IUnknown_AddRef((IUnknown*)*ppv);
643 return S_OK;
646 if (IsEqualIID(riid, &IID_IMarshal))
647 return IUnknown_QueryInterface(This->marshal, riid, ppv);
649 WARN("Unknown interface %s\n", debugstr_guid(riid));
650 return E_NOINTERFACE;
653 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
655 ACImpl *This = impl_from_IAudioClient3(iface);
656 ULONG ref;
657 ref = InterlockedIncrement(&This->ref);
658 TRACE("(%p) Refcount now %u\n", This, ref);
659 return ref;
662 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
664 ACImpl *This = impl_from_IAudioClient3(iface);
665 ULONG ref;
666 ref = InterlockedDecrement(&This->ref);
667 TRACE("(%p) Refcount now %u\n", This, ref);
668 if (!ref) {
669 if (This->pulse_stream) {
670 pulse_release_stream(This->pulse_stream, This->timer);
671 This->pulse_stream = NULL;
672 EnterCriticalSection(&session_cs);
673 list_remove(&This->entry);
674 LeaveCriticalSection(&session_cs);
676 IUnknown_Release(This->marshal);
677 IMMDevice_Release(This->parent);
678 HeapFree(GetProcessHeap(), 0, This);
680 return ref;
683 static void dump_fmt(const WAVEFORMATEX *fmt)
685 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
686 switch(fmt->wFormatTag) {
687 case WAVE_FORMAT_PCM:
688 TRACE("WAVE_FORMAT_PCM");
689 break;
690 case WAVE_FORMAT_IEEE_FLOAT:
691 TRACE("WAVE_FORMAT_IEEE_FLOAT");
692 break;
693 case WAVE_FORMAT_EXTENSIBLE:
694 TRACE("WAVE_FORMAT_EXTENSIBLE");
695 break;
696 default:
697 TRACE("Unknown");
698 break;
700 TRACE(")\n");
702 TRACE("nChannels: %u\n", fmt->nChannels);
703 TRACE("nSamplesPerSec: %u\n", fmt->nSamplesPerSec);
704 TRACE("nAvgBytesPerSec: %u\n", fmt->nAvgBytesPerSec);
705 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
706 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
707 TRACE("cbSize: %u\n", fmt->cbSize);
709 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
710 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
711 TRACE("dwChannelMask: %08x\n", fmtex->dwChannelMask);
712 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
713 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
717 static WAVEFORMATEX *clone_format(const WAVEFORMATEX *fmt)
719 WAVEFORMATEX *ret;
720 size_t size;
722 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
723 size = sizeof(WAVEFORMATEXTENSIBLE);
724 else
725 size = sizeof(WAVEFORMATEX);
727 ret = CoTaskMemAlloc(size);
728 if (!ret)
729 return NULL;
731 memcpy(ret, fmt, size);
733 ret->cbSize = size - sizeof(WAVEFORMATEX);
735 return ret;
738 static void session_init_vols(AudioSession *session, UINT channels)
740 if (session->channel_count < channels) {
741 UINT i;
743 if (session->channel_vols)
744 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
745 session->channel_vols, sizeof(float) * channels);
746 else
747 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
748 sizeof(float) * channels);
749 if (!session->channel_vols)
750 return;
752 for(i = session->channel_count; i < channels; ++i)
753 session->channel_vols[i] = 1.f;
755 session->channel_count = channels;
759 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
760 UINT num_channels)
762 AudioSession *ret;
764 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
765 if (!ret)
766 return NULL;
768 memcpy(&ret->guid, guid, sizeof(GUID));
770 ret->device = device;
772 list_init(&ret->clients);
774 list_add_head(&g_sessions, &ret->entry);
776 session_init_vols(ret, num_channels);
778 ret->master_vol = 1.f;
780 return ret;
783 /* if channels == 0, then this will return or create a session with
784 * matching dataflow and GUID. otherwise, channels must also match */
785 static HRESULT get_audio_session(const GUID *sessionguid,
786 IMMDevice *device, UINT channels, AudioSession **out)
788 AudioSession *session;
790 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
791 *out = create_session(&GUID_NULL, device, channels);
792 if (!*out)
793 return E_OUTOFMEMORY;
795 return S_OK;
798 *out = NULL;
799 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
800 if (session->device == device &&
801 IsEqualGUID(sessionguid, &session->guid)) {
802 session_init_vols(session, channels);
803 *out = session;
804 break;
808 if (!*out) {
809 *out = create_session(sessionguid, device, channels);
810 if (!*out)
811 return E_OUTOFMEMORY;
814 return S_OK;
817 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
818 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
819 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
820 const GUID *sessionguid)
822 ACImpl *This = impl_from_IAudioClient3(iface);
823 struct create_stream_params params;
824 unsigned int i, channel_count;
825 struct pulse_stream *stream;
826 char *name;
827 HRESULT hr;
829 TRACE("(%p)->(%x, %x, %s, %s, %p, %s)\n", This, mode, flags,
830 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
832 if (!fmt)
833 return E_POINTER;
834 dump_fmt(fmt);
836 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
837 return E_INVALIDARG;
838 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
839 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
841 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
842 AUDCLNT_STREAMFLAGS_LOOPBACK |
843 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
844 AUDCLNT_STREAMFLAGS_NOPERSIST |
845 AUDCLNT_STREAMFLAGS_RATEADJUST |
846 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
847 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
848 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
849 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
850 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)) {
851 FIXME("Unknown flags: %08x\n", flags);
852 return E_INVALIDARG;
855 EnterCriticalSection(&session_cs);
857 if (This->pulse_stream) {
858 LeaveCriticalSection(&session_cs);
859 return AUDCLNT_E_ALREADY_INITIALIZED;
862 if (!pulse_thread)
864 HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL);
865 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, event, 0, NULL)))
867 ERR("Failed to create mainloop thread.\n");
868 LeaveCriticalSection(&session_cs);
869 CloseHandle(event);
870 return E_FAIL;
872 SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
873 WaitForSingleObject(event, INFINITE);
874 CloseHandle(event);
877 params.name = name = get_application_name(TRUE);
878 params.pulse_name = This->pulse_name;
879 params.dataflow = This->dataflow;
880 params.mode = mode;
881 params.flags = flags;
882 params.duration = duration;
883 params.fmt = fmt;
884 params.stream = &stream;
885 params.channel_count = &channel_count;
886 pulse_call(create_stream, &params);
887 free(name);
888 if (FAILED(hr = params.result))
890 LeaveCriticalSection(&session_cs);
891 return hr;
894 if (!(This->vol = malloc(channel_count * sizeof(*This->vol))))
896 pulse_release_stream(stream, NULL);
897 LeaveCriticalSection(&session_cs);
898 return E_OUTOFMEMORY;
900 for (i = 0; i < channel_count; i++)
901 This->vol[i] = 1.f;
903 hr = get_audio_session(sessionguid, This->parent, channel_count, &This->session);
904 if (FAILED(hr))
906 free(This->vol);
907 This->vol = NULL;
908 LeaveCriticalSection(&session_cs);
909 pulse_release_stream(stream, NULL);
910 return E_OUTOFMEMORY;
913 This->pulse_stream = stream;
914 This->channel_count = channel_count;
915 list_add_tail(&This->session->clients, &This->entry);
916 set_stream_volumes(This);
918 LeaveCriticalSection(&session_cs);
919 return S_OK;
922 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
923 UINT32 *out)
925 ACImpl *This = impl_from_IAudioClient3(iface);
926 struct get_buffer_size_params params;
928 TRACE("(%p)->(%p)\n", This, out);
930 if (!out)
931 return E_POINTER;
932 if (!This->pulse_stream)
933 return AUDCLNT_E_NOT_INITIALIZED;
935 params.stream = This->pulse_stream;
936 params.size = out;
937 pulse_call(get_buffer_size, &params);
938 return params.result;
941 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
942 REFERENCE_TIME *latency)
944 ACImpl *This = impl_from_IAudioClient3(iface);
945 struct get_latency_params params;
947 TRACE("(%p)->(%p)\n", This, latency);
949 if (!latency)
950 return E_POINTER;
951 if (!This->pulse_stream)
952 return AUDCLNT_E_NOT_INITIALIZED;
954 params.stream = This->pulse_stream;
955 params.latency = latency;
956 pulse_call(get_latency, &params);
957 return params.result;
960 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
961 UINT32 *out)
963 ACImpl *This = impl_from_IAudioClient3(iface);
964 struct get_current_padding_params params;
966 TRACE("(%p)->(%p)\n", This, out);
968 if (!out)
969 return E_POINTER;
970 if (!This->pulse_stream)
971 return AUDCLNT_E_NOT_INITIALIZED;
973 params.stream = This->pulse_stream;
974 params.padding = out;
975 pulse_call(get_current_padding, &params);
976 return params.result;
979 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
980 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
981 WAVEFORMATEX **out)
983 ACImpl *This = impl_from_IAudioClient3(iface);
984 HRESULT hr = S_OK;
985 WAVEFORMATEX *closest = NULL;
986 BOOL exclusive;
988 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
990 if (!fmt)
991 return E_POINTER;
993 if (out)
994 *out = NULL;
996 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
997 exclusive = 1;
998 out = NULL;
999 } else if (mode == AUDCLNT_SHAREMODE_SHARED) {
1000 exclusive = 0;
1001 if (!out)
1002 return E_POINTER;
1003 } else
1004 return E_INVALIDARG;
1006 if (fmt->nChannels == 0)
1007 return AUDCLNT_E_UNSUPPORTED_FORMAT;
1009 closest = clone_format(fmt);
1010 if (!closest)
1011 return E_OUTOFMEMORY;
1013 dump_fmt(fmt);
1015 switch (fmt->wFormatTag) {
1016 case WAVE_FORMAT_EXTENSIBLE: {
1017 WAVEFORMATEXTENSIBLE *ext = (WAVEFORMATEXTENSIBLE*)closest;
1019 if ((fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX) &&
1020 fmt->cbSize != sizeof(WAVEFORMATEXTENSIBLE)) ||
1021 fmt->nBlockAlign != fmt->wBitsPerSample / 8 * fmt->nChannels ||
1022 ext->Samples.wValidBitsPerSample > fmt->wBitsPerSample ||
1023 fmt->nAvgBytesPerSec != fmt->nBlockAlign * fmt->nSamplesPerSec) {
1024 hr = E_INVALIDARG;
1025 break;
1028 if (exclusive) {
1029 UINT32 mask = 0, i, channels = 0;
1031 if (!(ext->dwChannelMask & (SPEAKER_ALL | SPEAKER_RESERVED))) {
1032 for (i = 1; !(i & SPEAKER_RESERVED); i <<= 1) {
1033 if (i & ext->dwChannelMask) {
1034 mask |= i;
1035 channels++;
1039 if (channels != fmt->nChannels || (ext->dwChannelMask & ~mask)) {
1040 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1041 break;
1043 } else {
1044 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1045 break;
1049 if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
1050 if (fmt->wBitsPerSample != 32) {
1051 hr = E_INVALIDARG;
1052 break;
1055 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample) {
1056 hr = S_FALSE;
1057 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1059 } else if (IsEqualGUID(&ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
1060 if (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8) {
1061 hr = E_INVALIDARG;
1062 break;
1065 if (ext->Samples.wValidBitsPerSample != fmt->wBitsPerSample &&
1066 !(fmt->wBitsPerSample == 32 &&
1067 ext->Samples.wValidBitsPerSample == 24)) {
1068 hr = S_FALSE;
1069 ext->Samples.wValidBitsPerSample = fmt->wBitsPerSample;
1070 break;
1072 } else {
1073 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1074 break;
1077 break;
1080 case WAVE_FORMAT_ALAW:
1081 case WAVE_FORMAT_MULAW:
1082 if (fmt->wBitsPerSample != 8) {
1083 hr = E_INVALIDARG;
1084 break;
1086 /* Fall-through */
1087 case WAVE_FORMAT_IEEE_FLOAT:
1088 if (fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT && fmt->wBitsPerSample != 32) {
1089 hr = E_INVALIDARG;
1090 break;
1092 /* Fall-through */
1093 case WAVE_FORMAT_PCM:
1094 if (fmt->wFormatTag == WAVE_FORMAT_PCM &&
1095 (!fmt->wBitsPerSample || fmt->wBitsPerSample > 32 || fmt->wBitsPerSample % 8)) {
1096 hr = E_INVALIDARG;
1097 break;
1100 if (fmt->nChannels > 2) {
1101 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1102 break;
1105 * fmt->cbSize, fmt->nBlockAlign and fmt->nAvgBytesPerSec seem to be
1106 * ignored, invalid values are happily accepted.
1108 break;
1109 default:
1110 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1111 break;
1114 if (exclusive && hr != S_OK) {
1115 hr = AUDCLNT_E_UNSUPPORTED_FORMAT;
1116 CoTaskMemFree(closest);
1117 } else if (hr != S_FALSE)
1118 CoTaskMemFree(closest);
1119 else
1120 *out = closest;
1122 /* Winepulse does not currently support exclusive mode, if you know of an
1123 * application that uses it, I will correct this..
1125 if (hr == S_OK && exclusive)
1126 return This->dataflow == eCapture ? AUDCLNT_E_UNSUPPORTED_FORMAT : AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
1128 TRACE("returning: %08x %p\n", hr, out ? *out : NULL);
1129 return hr;
1132 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
1133 WAVEFORMATEX **pwfx)
1135 ACImpl *This = impl_from_IAudioClient3(iface);
1137 TRACE("(%p)->(%p)\n", This, pwfx);
1139 if (!pwfx)
1140 return E_POINTER;
1142 *pwfx = clone_format(&pulse_config.modes[This->dataflow == eCapture].format.Format);
1143 if (!*pwfx)
1144 return E_OUTOFMEMORY;
1145 dump_fmt(*pwfx);
1146 return S_OK;
1149 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
1150 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
1152 ACImpl *This = impl_from_IAudioClient3(iface);
1154 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
1156 if (!defperiod && !minperiod)
1157 return E_POINTER;
1159 if (defperiod)
1160 *defperiod = pulse_config.modes[This->dataflow == eCapture].def_period;
1161 if (minperiod)
1162 *minperiod = pulse_config.modes[This->dataflow == eCapture].min_period;
1164 return S_OK;
1167 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
1169 ACImpl *This = impl_from_IAudioClient3(iface);
1170 struct start_params params;
1171 HRESULT hr;
1173 TRACE("(%p)\n", This);
1175 if (!This->pulse_stream)
1176 return AUDCLNT_E_NOT_INITIALIZED;
1178 params.stream = This->pulse_stream;
1179 pulse_call(start, &params);
1180 if (FAILED(hr = params.result))
1181 return hr;
1183 if (!This->timer) {
1184 This->timer = CreateThread(NULL, 0, pulse_timer_cb, This, 0, NULL);
1185 SetThreadPriority(This->timer, THREAD_PRIORITY_TIME_CRITICAL);
1188 return S_OK;
1191 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
1193 ACImpl *This = impl_from_IAudioClient3(iface);
1194 struct stop_params params;
1196 TRACE("(%p)\n", This);
1198 if (!This->pulse_stream)
1199 return AUDCLNT_E_NOT_INITIALIZED;
1201 params.stream = This->pulse_stream;
1202 pulse_call(stop, &params);
1203 return params.result;
1206 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
1208 ACImpl *This = impl_from_IAudioClient3(iface);
1209 struct reset_params params;
1211 TRACE("(%p)\n", This);
1213 if (!This->pulse_stream)
1214 return AUDCLNT_E_NOT_INITIALIZED;
1216 params.stream = This->pulse_stream;
1217 pulse_call(reset, &params);
1218 return params.result;
1221 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
1222 HANDLE event)
1224 ACImpl *This = impl_from_IAudioClient3(iface);
1225 struct set_event_handle_params params;
1227 TRACE("(%p)->(%p)\n", This, event);
1229 if (!event)
1230 return E_INVALIDARG;
1231 if (!This->pulse_stream)
1232 return AUDCLNT_E_NOT_INITIALIZED;
1234 params.stream = This->pulse_stream;
1235 params.event = event;
1236 pulse_call(set_event_handle, &params);
1237 return params.result;
1240 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
1241 void **ppv)
1243 ACImpl *This = impl_from_IAudioClient3(iface);
1245 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1247 if (!ppv)
1248 return E_POINTER;
1249 *ppv = NULL;
1251 if (!This->pulse_stream)
1252 return AUDCLNT_E_NOT_INITIALIZED;
1254 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1255 if (This->dataflow != eRender)
1256 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1257 *ppv = &This->IAudioRenderClient_iface;
1258 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1259 if (This->dataflow != eCapture)
1260 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1261 *ppv = &This->IAudioCaptureClient_iface;
1262 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1263 *ppv = &This->IAudioClock_iface;
1264 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1265 *ppv = &This->IAudioStreamVolume_iface;
1266 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1267 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1268 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1269 if (!This->session_wrapper) {
1270 This->session_wrapper = AudioSessionWrapper_Create(This);
1271 if (!This->session_wrapper)
1272 return E_OUTOFMEMORY;
1274 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1275 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1276 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1277 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1278 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1279 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1282 if (*ppv) {
1283 IUnknown_AddRef((IUnknown*)*ppv);
1284 return S_OK;
1287 FIXME("stub %s\n", debugstr_guid(riid));
1288 return E_NOINTERFACE;
1291 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
1292 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
1294 ACImpl *This = impl_from_IAudioClient3(iface);
1296 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
1298 if(!offload_capable)
1299 return E_INVALIDARG;
1301 *offload_capable = FALSE;
1303 return S_OK;
1306 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
1307 const AudioClientProperties *prop)
1309 ACImpl *This = impl_from_IAudioClient3(iface);
1310 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
1312 TRACE("(%p)->(%p)\n", This, prop);
1314 if(!legacy_prop)
1315 return E_POINTER;
1317 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
1318 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
1319 legacy_prop->bIsOffload,
1320 legacy_prop->eCategory,
1321 prop->Options);
1322 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
1323 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
1324 legacy_prop->bIsOffload,
1325 legacy_prop->eCategory);
1326 }else{
1327 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
1328 return E_INVALIDARG;
1332 if(legacy_prop->bIsOffload)
1333 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
1335 return S_OK;
1338 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
1339 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
1340 REFERENCE_TIME *max_duration)
1342 ACImpl *This = impl_from_IAudioClient3(iface);
1344 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
1346 return E_NOTIMPL;
1349 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
1350 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
1351 UINT32 *min_period_frames, UINT32 *max_period_frames)
1353 ACImpl *This = impl_from_IAudioClient3(iface);
1355 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
1356 min_period_frames, max_period_frames);
1358 return E_NOTIMPL;
1361 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
1362 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
1364 ACImpl *This = impl_from_IAudioClient3(iface);
1366 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1368 return E_NOTIMPL;
1371 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
1372 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
1373 const GUID *session_guid)
1375 ACImpl *This = impl_from_IAudioClient3(iface);
1377 FIXME("(%p)->(0x%x, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
1379 return E_NOTIMPL;
1382 static const IAudioClient3Vtbl AudioClient3_Vtbl =
1384 AudioClient_QueryInterface,
1385 AudioClient_AddRef,
1386 AudioClient_Release,
1387 AudioClient_Initialize,
1388 AudioClient_GetBufferSize,
1389 AudioClient_GetStreamLatency,
1390 AudioClient_GetCurrentPadding,
1391 AudioClient_IsFormatSupported,
1392 AudioClient_GetMixFormat,
1393 AudioClient_GetDevicePeriod,
1394 AudioClient_Start,
1395 AudioClient_Stop,
1396 AudioClient_Reset,
1397 AudioClient_SetEventHandle,
1398 AudioClient_GetService,
1399 AudioClient_IsOffloadCapable,
1400 AudioClient_SetClientProperties,
1401 AudioClient_GetBufferSizeLimits,
1402 AudioClient_GetSharedModeEnginePeriod,
1403 AudioClient_GetCurrentSharedModeEnginePeriod,
1404 AudioClient_InitializeSharedAudioStream,
1407 static HRESULT WINAPI AudioRenderClient_QueryInterface(
1408 IAudioRenderClient *iface, REFIID riid, void **ppv)
1410 ACImpl *This = impl_from_IAudioRenderClient(iface);
1411 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1413 if (!ppv)
1414 return E_POINTER;
1415 *ppv = NULL;
1417 if (IsEqualIID(riid, &IID_IUnknown) ||
1418 IsEqualIID(riid, &IID_IAudioRenderClient))
1419 *ppv = iface;
1420 if (*ppv) {
1421 IUnknown_AddRef((IUnknown*)*ppv);
1422 return S_OK;
1425 if (IsEqualIID(riid, &IID_IMarshal))
1426 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1428 WARN("Unknown interface %s\n", debugstr_guid(riid));
1429 return E_NOINTERFACE;
1432 static ULONG WINAPI AudioRenderClient_AddRef(IAudioRenderClient *iface)
1434 ACImpl *This = impl_from_IAudioRenderClient(iface);
1435 return AudioClient_AddRef(&This->IAudioClient3_iface);
1438 static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
1440 ACImpl *This = impl_from_IAudioRenderClient(iface);
1441 return AudioClient_Release(&This->IAudioClient3_iface);
1444 static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
1445 UINT32 frames, BYTE **data)
1447 ACImpl *This = impl_from_IAudioRenderClient(iface);
1448 struct get_render_buffer_params params;
1450 TRACE("(%p)->(%u, %p)\n", This, frames, data);
1452 if (!data)
1453 return E_POINTER;
1454 if (!This->pulse_stream)
1455 return AUDCLNT_E_NOT_INITIALIZED;
1456 *data = NULL;
1458 params.stream = This->pulse_stream;
1459 params.frames = frames;
1460 params.data = data;
1461 pulse_call(get_render_buffer, &params);
1462 return params.result;
1465 static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
1466 IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
1468 ACImpl *This = impl_from_IAudioRenderClient(iface);
1469 struct release_render_buffer_params params;
1471 TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
1473 if (!This->pulse_stream)
1474 return AUDCLNT_E_NOT_INITIALIZED;
1476 params.stream = This->pulse_stream;
1477 params.written_frames = written_frames;
1478 params.flags = flags;
1479 pulse_call(release_render_buffer, &params);
1480 return params.result;
1483 static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
1484 AudioRenderClient_QueryInterface,
1485 AudioRenderClient_AddRef,
1486 AudioRenderClient_Release,
1487 AudioRenderClient_GetBuffer,
1488 AudioRenderClient_ReleaseBuffer
1491 static HRESULT WINAPI AudioCaptureClient_QueryInterface(
1492 IAudioCaptureClient *iface, REFIID riid, void **ppv)
1494 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1495 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1497 if (!ppv)
1498 return E_POINTER;
1499 *ppv = NULL;
1501 if (IsEqualIID(riid, &IID_IUnknown) ||
1502 IsEqualIID(riid, &IID_IAudioCaptureClient))
1503 *ppv = iface;
1504 if (*ppv) {
1505 IUnknown_AddRef((IUnknown*)*ppv);
1506 return S_OK;
1509 if (IsEqualIID(riid, &IID_IMarshal))
1510 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1512 WARN("Unknown interface %s\n", debugstr_guid(riid));
1513 return E_NOINTERFACE;
1516 static ULONG WINAPI AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
1518 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1519 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1522 static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
1524 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1525 return IAudioClient3_Release(&This->IAudioClient3_iface);
1528 static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
1529 BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
1530 UINT64 *qpcpos)
1532 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1533 struct get_capture_buffer_params params;
1535 TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
1536 devpos, qpcpos);
1538 if (!data)
1539 return E_POINTER;
1540 *data = NULL;
1541 if (!frames || !flags)
1542 return E_POINTER;
1543 if (!This->pulse_stream)
1544 return AUDCLNT_E_NOT_INITIALIZED;
1546 params.stream = This->pulse_stream;
1547 params.data = data;
1548 params.frames = frames;
1549 params.flags = flags;
1550 params.devpos = devpos;
1551 params.qpcpos = qpcpos;
1552 pulse_call(get_capture_buffer, &params);
1553 return params.result;
1556 static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
1557 IAudioCaptureClient *iface, UINT32 done)
1559 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1560 struct release_capture_buffer_params params;
1562 TRACE("(%p)->(%u)\n", This, done);
1564 if (!This->pulse_stream)
1565 return AUDCLNT_E_NOT_INITIALIZED;
1567 params.stream = This->pulse_stream;
1568 params.done = done;
1569 pulse_call(release_capture_buffer, &params);
1570 return params.result;
1573 static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
1574 IAudioCaptureClient *iface, UINT32 *frames)
1576 ACImpl *This = impl_from_IAudioCaptureClient(iface);
1577 struct get_next_packet_size_params params;
1579 TRACE("(%p)->(%p)\n", This, frames);
1581 if (!frames)
1582 return E_POINTER;
1583 if (!This->pulse_stream)
1584 return AUDCLNT_E_NOT_INITIALIZED;
1586 params.stream = This->pulse_stream;
1587 params.frames = frames;
1588 pulse_call(get_next_packet_size, &params);
1589 return params.result;
1592 static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
1594 AudioCaptureClient_QueryInterface,
1595 AudioCaptureClient_AddRef,
1596 AudioCaptureClient_Release,
1597 AudioCaptureClient_GetBuffer,
1598 AudioCaptureClient_ReleaseBuffer,
1599 AudioCaptureClient_GetNextPacketSize
1602 static HRESULT WINAPI AudioClock_QueryInterface(IAudioClock *iface,
1603 REFIID riid, void **ppv)
1605 ACImpl *This = impl_from_IAudioClock(iface);
1607 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1609 if (!ppv)
1610 return E_POINTER;
1611 *ppv = NULL;
1613 if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IAudioClock))
1614 *ppv = iface;
1615 else if (IsEqualIID(riid, &IID_IAudioClock2))
1616 *ppv = &This->IAudioClock2_iface;
1617 if (*ppv) {
1618 IUnknown_AddRef((IUnknown*)*ppv);
1619 return S_OK;
1622 if (IsEqualIID(riid, &IID_IMarshal))
1623 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1625 WARN("Unknown interface %s\n", debugstr_guid(riid));
1626 return E_NOINTERFACE;
1629 static ULONG WINAPI AudioClock_AddRef(IAudioClock *iface)
1631 ACImpl *This = impl_from_IAudioClock(iface);
1632 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1635 static ULONG WINAPI AudioClock_Release(IAudioClock *iface)
1637 ACImpl *This = impl_from_IAudioClock(iface);
1638 return IAudioClient3_Release(&This->IAudioClient3_iface);
1641 static HRESULT WINAPI AudioClock_GetFrequency(IAudioClock *iface, UINT64 *freq)
1643 ACImpl *This = impl_from_IAudioClock(iface);
1644 struct get_frequency_params params;
1646 TRACE("(%p)->(%p)\n", This, freq);
1648 if (!This->pulse_stream)
1649 return AUDCLNT_E_NOT_INITIALIZED;
1651 params.stream = This->pulse_stream;
1652 params.freq = freq;
1653 pulse_call(get_frequency, &params);
1654 return params.result;
1657 static HRESULT WINAPI AudioClock_GetPosition(IAudioClock *iface, UINT64 *pos,
1658 UINT64 *qpctime)
1660 ACImpl *This = impl_from_IAudioClock(iface);
1661 struct get_position_params params;
1663 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1665 if (!pos)
1666 return E_POINTER;
1667 if (!This->pulse_stream)
1668 return AUDCLNT_E_NOT_INITIALIZED;
1670 params.stream = This->pulse_stream;
1671 params.device = FALSE;
1672 params.pos = pos;
1673 params.qpctime = qpctime;
1674 pulse_call(get_position, &params);
1675 return params.result;
1678 static HRESULT WINAPI AudioClock_GetCharacteristics(IAudioClock *iface,
1679 DWORD *chars)
1681 ACImpl *This = impl_from_IAudioClock(iface);
1683 TRACE("(%p)->(%p)\n", This, chars);
1685 if (!chars)
1686 return E_POINTER;
1688 *chars = AUDIOCLOCK_CHARACTERISTIC_FIXED_FREQ;
1690 return S_OK;
1693 static const IAudioClockVtbl AudioClock_Vtbl =
1695 AudioClock_QueryInterface,
1696 AudioClock_AddRef,
1697 AudioClock_Release,
1698 AudioClock_GetFrequency,
1699 AudioClock_GetPosition,
1700 AudioClock_GetCharacteristics
1703 static HRESULT WINAPI AudioClock2_QueryInterface(IAudioClock2 *iface,
1704 REFIID riid, void **ppv)
1706 ACImpl *This = impl_from_IAudioClock2(iface);
1707 return IAudioClock_QueryInterface(&This->IAudioClock_iface, riid, ppv);
1710 static ULONG WINAPI AudioClock2_AddRef(IAudioClock2 *iface)
1712 ACImpl *This = impl_from_IAudioClock2(iface);
1713 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1716 static ULONG WINAPI AudioClock2_Release(IAudioClock2 *iface)
1718 ACImpl *This = impl_from_IAudioClock2(iface);
1719 return IAudioClient3_Release(&This->IAudioClient3_iface);
1722 static HRESULT WINAPI AudioClock2_GetDevicePosition(IAudioClock2 *iface,
1723 UINT64 *pos, UINT64 *qpctime)
1725 ACImpl *This = impl_from_IAudioClock2(iface);
1726 struct get_position_params params;
1728 TRACE("(%p)->(%p, %p)\n", This, pos, qpctime);
1730 if (!pos)
1731 return E_POINTER;
1732 if (!This->pulse_stream)
1733 return AUDCLNT_E_NOT_INITIALIZED;
1735 params.stream = This->pulse_stream;
1736 params.device = TRUE;
1737 params.pos = pos;
1738 params.qpctime = qpctime;
1739 pulse_call(get_position, &params);
1740 return params.result;
1743 static const IAudioClock2Vtbl AudioClock2_Vtbl =
1745 AudioClock2_QueryInterface,
1746 AudioClock2_AddRef,
1747 AudioClock2_Release,
1748 AudioClock2_GetDevicePosition
1751 static HRESULT WINAPI AudioStreamVolume_QueryInterface(
1752 IAudioStreamVolume *iface, REFIID riid, void **ppv)
1754 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1756 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1758 if (!ppv)
1759 return E_POINTER;
1760 *ppv = NULL;
1762 if (IsEqualIID(riid, &IID_IUnknown) ||
1763 IsEqualIID(riid, &IID_IAudioStreamVolume))
1764 *ppv = iface;
1765 if (*ppv) {
1766 IUnknown_AddRef((IUnknown*)*ppv);
1767 return S_OK;
1770 if (IsEqualIID(riid, &IID_IMarshal))
1771 return IUnknown_QueryInterface(This->marshal, riid, ppv);
1773 WARN("Unknown interface %s\n", debugstr_guid(riid));
1774 return E_NOINTERFACE;
1777 static ULONG WINAPI AudioStreamVolume_AddRef(IAudioStreamVolume *iface)
1779 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1780 return IAudioClient3_AddRef(&This->IAudioClient3_iface);
1783 static ULONG WINAPI AudioStreamVolume_Release(IAudioStreamVolume *iface)
1785 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1786 return IAudioClient3_Release(&This->IAudioClient3_iface);
1789 static HRESULT WINAPI AudioStreamVolume_GetChannelCount(
1790 IAudioStreamVolume *iface, UINT32 *out)
1792 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1794 TRACE("(%p)->(%p)\n", This, out);
1796 if (!out)
1797 return E_POINTER;
1799 *out = This->channel_count;
1801 return S_OK;
1804 struct pulse_info_cb_data {
1805 UINT32 n;
1806 float *levels;
1809 static HRESULT WINAPI AudioStreamVolume_SetAllVolumes(
1810 IAudioStreamVolume *iface, UINT32 count, const float *levels)
1812 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1813 int i;
1815 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1817 if (!levels)
1818 return E_POINTER;
1820 if (!This->pulse_stream)
1821 return AUDCLNT_E_NOT_INITIALIZED;
1822 if (count != This->channel_count)
1823 return E_INVALIDARG;
1825 EnterCriticalSection(&session_cs);
1826 for (i = 0; i < count; ++i)
1827 This->vol[i] = levels[i];
1829 set_stream_volumes(This);
1830 LeaveCriticalSection(&session_cs);
1831 return S_OK;
1834 static HRESULT WINAPI AudioStreamVolume_GetAllVolumes(
1835 IAudioStreamVolume *iface, UINT32 count, float *levels)
1837 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1838 int i;
1840 TRACE("(%p)->(%d, %p)\n", This, count, levels);
1842 if (!levels)
1843 return E_POINTER;
1845 if (!This->pulse_stream)
1846 return AUDCLNT_E_NOT_INITIALIZED;
1847 if (count != This->channel_count)
1848 return E_INVALIDARG;
1850 EnterCriticalSection(&session_cs);
1851 for (i = 0; i < count; ++i)
1852 levels[i] = This->vol[i];
1853 LeaveCriticalSection(&session_cs);
1854 return S_OK;
1857 static HRESULT WINAPI AudioStreamVolume_SetChannelVolume(
1858 IAudioStreamVolume *iface, UINT32 index, float level)
1860 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1862 TRACE("(%p)->(%d, %f)\n", This, index, level);
1864 if (level < 0.f || level > 1.f)
1865 return E_INVALIDARG;
1867 if (!This->pulse_stream)
1868 return AUDCLNT_E_NOT_INITIALIZED;
1869 if (index >= This->channel_count)
1870 return E_INVALIDARG;
1872 EnterCriticalSection(&session_cs);
1873 This->vol[index] = level;
1874 set_stream_volumes(This);
1875 LeaveCriticalSection(&session_cs);
1876 return S_OK;
1879 static HRESULT WINAPI AudioStreamVolume_GetChannelVolume(
1880 IAudioStreamVolume *iface, UINT32 index, float *level)
1882 ACImpl *This = impl_from_IAudioStreamVolume(iface);
1884 TRACE("(%p)->(%d, %p)\n", This, index, level);
1886 if (!level)
1887 return E_POINTER;
1889 if (!This->pulse_stream)
1890 return AUDCLNT_E_NOT_INITIALIZED;
1891 if (index >= This->channel_count)
1892 return E_INVALIDARG;
1894 EnterCriticalSection(&session_cs);
1895 *level = This->vol[index];
1896 LeaveCriticalSection(&session_cs);
1897 return S_OK;
1900 static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl =
1902 AudioStreamVolume_QueryInterface,
1903 AudioStreamVolume_AddRef,
1904 AudioStreamVolume_Release,
1905 AudioStreamVolume_GetChannelCount,
1906 AudioStreamVolume_SetChannelVolume,
1907 AudioStreamVolume_GetChannelVolume,
1908 AudioStreamVolume_SetAllVolumes,
1909 AudioStreamVolume_GetAllVolumes
1912 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1914 AudioSessionWrapper *ret;
1916 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1917 sizeof(AudioSessionWrapper));
1918 if (!ret)
1919 return NULL;
1921 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1922 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
1923 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
1925 ret->ref = !client;
1927 ret->client = client;
1928 if (client) {
1929 ret->session = client->session;
1930 AudioClient_AddRef(&client->IAudioClient3_iface);
1933 return ret;
1936 static HRESULT WINAPI AudioSessionControl_QueryInterface(
1937 IAudioSessionControl2 *iface, REFIID riid, void **ppv)
1939 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
1941 if (!ppv)
1942 return E_POINTER;
1943 *ppv = NULL;
1945 if (IsEqualIID(riid, &IID_IUnknown) ||
1946 IsEqualIID(riid, &IID_IAudioSessionControl) ||
1947 IsEqualIID(riid, &IID_IAudioSessionControl2))
1948 *ppv = iface;
1949 if (*ppv) {
1950 IUnknown_AddRef((IUnknown*)*ppv);
1951 return S_OK;
1954 WARN("Unknown interface %s\n", debugstr_guid(riid));
1955 return E_NOINTERFACE;
1958 static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface)
1960 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1961 ULONG ref;
1962 ref = InterlockedIncrement(&This->ref);
1963 TRACE("(%p) Refcount now %u\n", This, ref);
1964 return ref;
1967 static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface)
1969 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1970 ULONG ref;
1971 ref = InterlockedDecrement(&This->ref);
1972 TRACE("(%p) Refcount now %u\n", This, ref);
1973 if (!ref) {
1974 if (This->client) {
1975 This->client->session_wrapper = NULL;
1976 AudioClient_Release(&This->client->IAudioClient3_iface);
1978 HeapFree(GetProcessHeap(), 0, This);
1980 return ref;
1983 static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface,
1984 AudioSessionState *state)
1986 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
1987 ACImpl *client;
1989 TRACE("(%p)->(%p)\n", This, state);
1991 if (!state)
1992 return NULL_PTR_ERR;
1994 EnterCriticalSection(&session_cs);
1995 if (list_empty(&This->session->clients)) {
1996 *state = AudioSessionStateExpired;
1997 goto out;
1999 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) {
2000 struct is_started_params params;
2002 if (!client->pulse_stream)
2003 continue;
2005 params.stream = client->pulse_stream;
2006 pulse_call(is_started, &params);
2007 if (params.started) {
2008 *state = AudioSessionStateActive;
2009 goto out;
2012 *state = AudioSessionStateInactive;
2014 out:
2015 LeaveCriticalSection(&session_cs);
2016 return S_OK;
2019 static HRESULT WINAPI AudioSessionControl_GetDisplayName(
2020 IAudioSessionControl2 *iface, WCHAR **name)
2022 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2024 FIXME("(%p)->(%p) - stub\n", This, name);
2026 return E_NOTIMPL;
2029 static HRESULT WINAPI AudioSessionControl_SetDisplayName(
2030 IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session)
2032 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2034 FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session));
2036 return E_NOTIMPL;
2039 static HRESULT WINAPI AudioSessionControl_GetIconPath(
2040 IAudioSessionControl2 *iface, WCHAR **path)
2042 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2044 FIXME("(%p)->(%p) - stub\n", This, path);
2046 return E_NOTIMPL;
2049 static HRESULT WINAPI AudioSessionControl_SetIconPath(
2050 IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session)
2052 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2054 FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session));
2056 return E_NOTIMPL;
2059 static HRESULT WINAPI AudioSessionControl_GetGroupingParam(
2060 IAudioSessionControl2 *iface, GUID *group)
2062 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2064 FIXME("(%p)->(%p) - stub\n", This, group);
2066 return E_NOTIMPL;
2069 static HRESULT WINAPI AudioSessionControl_SetGroupingParam(
2070 IAudioSessionControl2 *iface, const GUID *group, const GUID *session)
2072 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2074 FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group),
2075 debugstr_guid(session));
2077 return E_NOTIMPL;
2080 static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification(
2081 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2083 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2085 FIXME("(%p)->(%p) - stub\n", This, events);
2087 return S_OK;
2090 static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification(
2091 IAudioSessionControl2 *iface, IAudioSessionEvents *events)
2093 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2095 FIXME("(%p)->(%p) - stub\n", This, events);
2097 return S_OK;
2100 static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier(
2101 IAudioSessionControl2 *iface, WCHAR **id)
2103 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2105 FIXME("(%p)->(%p) - stub\n", This, id);
2107 return E_NOTIMPL;
2110 static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier(
2111 IAudioSessionControl2 *iface, WCHAR **id)
2113 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2115 FIXME("(%p)->(%p) - stub\n", This, id);
2117 return E_NOTIMPL;
2120 static HRESULT WINAPI AudioSessionControl_GetProcessId(
2121 IAudioSessionControl2 *iface, DWORD *pid)
2123 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2125 TRACE("(%p)->(%p)\n", This, pid);
2127 if (!pid)
2128 return E_POINTER;
2130 *pid = GetCurrentProcessId();
2132 return S_OK;
2135 static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession(
2136 IAudioSessionControl2 *iface)
2138 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2140 TRACE("(%p)\n", This);
2142 return S_FALSE;
2145 static HRESULT WINAPI AudioSessionControl_SetDuckingPreference(
2146 IAudioSessionControl2 *iface, BOOL optout)
2148 AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface);
2150 TRACE("(%p)->(%d)\n", This, optout);
2152 return S_OK;
2155 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl =
2157 AudioSessionControl_QueryInterface,
2158 AudioSessionControl_AddRef,
2159 AudioSessionControl_Release,
2160 AudioSessionControl_GetState,
2161 AudioSessionControl_GetDisplayName,
2162 AudioSessionControl_SetDisplayName,
2163 AudioSessionControl_GetIconPath,
2164 AudioSessionControl_SetIconPath,
2165 AudioSessionControl_GetGroupingParam,
2166 AudioSessionControl_SetGroupingParam,
2167 AudioSessionControl_RegisterAudioSessionNotification,
2168 AudioSessionControl_UnregisterAudioSessionNotification,
2169 AudioSessionControl_GetSessionIdentifier,
2170 AudioSessionControl_GetSessionInstanceIdentifier,
2171 AudioSessionControl_GetProcessId,
2172 AudioSessionControl_IsSystemSoundsSession,
2173 AudioSessionControl_SetDuckingPreference
2176 typedef struct _SessionMgr {
2177 IAudioSessionManager2 IAudioSessionManager2_iface;
2179 LONG ref;
2181 IMMDevice *device;
2182 } SessionMgr;
2184 static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface,
2185 REFIID riid, void **ppv)
2187 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2189 if (!ppv)
2190 return E_POINTER;
2191 *ppv = NULL;
2193 if (IsEqualIID(riid, &IID_IUnknown) ||
2194 IsEqualIID(riid, &IID_IAudioSessionManager) ||
2195 IsEqualIID(riid, &IID_IAudioSessionManager2))
2196 *ppv = iface;
2197 if (*ppv) {
2198 IUnknown_AddRef((IUnknown*)*ppv);
2199 return S_OK;
2202 WARN("Unknown interface %s\n", debugstr_guid(riid));
2203 return E_NOINTERFACE;
2206 static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface)
2208 return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface);
2211 static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface)
2213 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2214 ULONG ref;
2215 ref = InterlockedIncrement(&This->ref);
2216 TRACE("(%p) Refcount now %u\n", This, ref);
2217 return ref;
2220 static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface)
2222 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2223 ULONG ref;
2224 ref = InterlockedDecrement(&This->ref);
2225 TRACE("(%p) Refcount now %u\n", This, ref);
2226 if (!ref)
2227 HeapFree(GetProcessHeap(), 0, This);
2228 return ref;
2231 static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl(
2232 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2233 IAudioSessionControl **out)
2235 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2236 AudioSession *session;
2237 AudioSessionWrapper *wrapper;
2238 HRESULT hr;
2240 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2241 flags, out);
2243 hr = get_audio_session(session_guid, This->device, 0, &session);
2244 if (FAILED(hr))
2245 return hr;
2247 wrapper = AudioSessionWrapper_Create(NULL);
2248 if (!wrapper)
2249 return E_OUTOFMEMORY;
2251 wrapper->session = session;
2253 *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface;
2255 return S_OK;
2258 static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume(
2259 IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags,
2260 ISimpleAudioVolume **out)
2262 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2263 AudioSession *session;
2264 AudioSessionWrapper *wrapper;
2265 HRESULT hr;
2267 TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid),
2268 flags, out);
2270 hr = get_audio_session(session_guid, This->device, 0, &session);
2271 if (FAILED(hr))
2272 return hr;
2274 wrapper = AudioSessionWrapper_Create(NULL);
2275 if (!wrapper)
2276 return E_OUTOFMEMORY;
2278 wrapper->session = session;
2280 *out = &wrapper->ISimpleAudioVolume_iface;
2282 return S_OK;
2285 static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator(
2286 IAudioSessionManager2 *iface, IAudioSessionEnumerator **out)
2288 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2289 FIXME("(%p)->(%p) - stub\n", This, out);
2290 return E_NOTIMPL;
2293 static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification(
2294 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2296 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2297 FIXME("(%p)->(%p) - stub\n", This, notification);
2298 return E_NOTIMPL;
2301 static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification(
2302 IAudioSessionManager2 *iface, IAudioSessionNotification *notification)
2304 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2305 FIXME("(%p)->(%p) - stub\n", This, notification);
2306 return E_NOTIMPL;
2309 static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification(
2310 IAudioSessionManager2 *iface, const WCHAR *session_id,
2311 IAudioVolumeDuckNotification *notification)
2313 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2314 FIXME("(%p)->(%p) - stub\n", This, notification);
2315 return E_NOTIMPL;
2318 static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification(
2319 IAudioSessionManager2 *iface,
2320 IAudioVolumeDuckNotification *notification)
2322 SessionMgr *This = impl_from_IAudioSessionManager2(iface);
2323 FIXME("(%p)->(%p) - stub\n", This, notification);
2324 return E_NOTIMPL;
2327 static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl =
2329 AudioSessionManager_QueryInterface,
2330 AudioSessionManager_AddRef,
2331 AudioSessionManager_Release,
2332 AudioSessionManager_GetAudioSessionControl,
2333 AudioSessionManager_GetSimpleAudioVolume,
2334 AudioSessionManager_GetSessionEnumerator,
2335 AudioSessionManager_RegisterSessionNotification,
2336 AudioSessionManager_UnregisterSessionNotification,
2337 AudioSessionManager_RegisterDuckNotification,
2338 AudioSessionManager_UnregisterDuckNotification
2341 static HRESULT WINAPI SimpleAudioVolume_QueryInterface(
2342 ISimpleAudioVolume *iface, REFIID riid, void **ppv)
2344 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2346 if (!ppv)
2347 return E_POINTER;
2348 *ppv = NULL;
2350 if (IsEqualIID(riid, &IID_IUnknown) ||
2351 IsEqualIID(riid, &IID_ISimpleAudioVolume))
2352 *ppv = iface;
2353 if (*ppv) {
2354 IUnknown_AddRef((IUnknown*)*ppv);
2355 return S_OK;
2358 WARN("Unknown interface %s\n", debugstr_guid(riid));
2359 return E_NOINTERFACE;
2362 static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface)
2364 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2365 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2368 static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface)
2370 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2371 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2374 static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume(
2375 ISimpleAudioVolume *iface, float level, const GUID *context)
2377 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2378 AudioSession *session = This->session;
2379 ACImpl *client;
2381 TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context));
2383 if (level < 0.f || level > 1.f)
2384 return E_INVALIDARG;
2386 if (context)
2387 FIXME("Notifications not supported yet\n");
2389 TRACE("PulseAudio does not support session volume control\n");
2391 EnterCriticalSection(&session_cs);
2392 session->master_vol = level;
2393 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2394 set_stream_volumes(client);
2395 LeaveCriticalSection(&session_cs);
2397 return S_OK;
2400 static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume(
2401 ISimpleAudioVolume *iface, float *level)
2403 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2404 AudioSession *session = This->session;
2406 TRACE("(%p)->(%p)\n", session, level);
2408 if (!level)
2409 return NULL_PTR_ERR;
2411 *level = session->master_vol;
2413 return S_OK;
2416 static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface,
2417 BOOL mute, const GUID *context)
2419 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2420 AudioSession *session = This->session;
2421 ACImpl *client;
2423 TRACE("(%p)->(%u, %s)\n", session, mute, debugstr_guid(context));
2425 if (context)
2426 FIXME("Notifications not supported yet\n");
2428 EnterCriticalSection(&session_cs);
2429 session->mute = mute;
2430 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2431 set_stream_volumes(client);
2432 LeaveCriticalSection(&session_cs);
2434 return S_OK;
2437 static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface,
2438 BOOL *mute)
2440 AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface);
2441 AudioSession *session = This->session;
2443 TRACE("(%p)->(%p)\n", session, mute);
2445 if (!mute)
2446 return NULL_PTR_ERR;
2448 *mute = session->mute;
2450 return S_OK;
2453 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl =
2455 SimpleAudioVolume_QueryInterface,
2456 SimpleAudioVolume_AddRef,
2457 SimpleAudioVolume_Release,
2458 SimpleAudioVolume_SetMasterVolume,
2459 SimpleAudioVolume_GetMasterVolume,
2460 SimpleAudioVolume_SetMute,
2461 SimpleAudioVolume_GetMute
2464 static HRESULT WINAPI ChannelAudioVolume_QueryInterface(
2465 IChannelAudioVolume *iface, REFIID riid, void **ppv)
2467 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
2469 if (!ppv)
2470 return E_POINTER;
2471 *ppv = NULL;
2473 if (IsEqualIID(riid, &IID_IUnknown) ||
2474 IsEqualIID(riid, &IID_IChannelAudioVolume))
2475 *ppv = iface;
2476 if (*ppv) {
2477 IUnknown_AddRef((IUnknown*)*ppv);
2478 return S_OK;
2481 WARN("Unknown interface %s\n", debugstr_guid(riid));
2482 return E_NOINTERFACE;
2485 static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface)
2487 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2488 return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface);
2491 static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface)
2493 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2494 return AudioSessionControl_Release(&This->IAudioSessionControl2_iface);
2497 static HRESULT WINAPI ChannelAudioVolume_GetChannelCount(
2498 IChannelAudioVolume *iface, UINT32 *out)
2500 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2501 AudioSession *session = This->session;
2503 TRACE("(%p)->(%p)\n", session, out);
2505 if (!out)
2506 return NULL_PTR_ERR;
2508 *out = session->channel_count;
2510 return S_OK;
2513 static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume(
2514 IChannelAudioVolume *iface, UINT32 index, float level,
2515 const GUID *context)
2517 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2518 AudioSession *session = This->session;
2519 ACImpl *client;
2521 TRACE("(%p)->(%d, %f, %s)\n", session, index, level,
2522 wine_dbgstr_guid(context));
2524 if (level < 0.f || level > 1.f)
2525 return E_INVALIDARG;
2527 if (index >= session->channel_count)
2528 return E_INVALIDARG;
2530 if (context)
2531 FIXME("Notifications not supported yet\n");
2533 TRACE("PulseAudio does not support session volume control\n");
2535 EnterCriticalSection(&session_cs);
2536 session->channel_vols[index] = level;
2537 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2538 set_stream_volumes(client);
2539 LeaveCriticalSection(&session_cs);
2541 return S_OK;
2544 static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume(
2545 IChannelAudioVolume *iface, UINT32 index, float *level)
2547 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2548 AudioSession *session = This->session;
2550 TRACE("(%p)->(%d, %p)\n", session, index, level);
2552 if (!level)
2553 return NULL_PTR_ERR;
2555 if (index >= session->channel_count)
2556 return E_INVALIDARG;
2558 *level = session->channel_vols[index];
2560 return S_OK;
2563 static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes(
2564 IChannelAudioVolume *iface, UINT32 count, const float *levels,
2565 const GUID *context)
2567 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2568 AudioSession *session = This->session;
2569 ACImpl *client;
2570 int i;
2572 TRACE("(%p)->(%d, %p, %s)\n", session, count, levels,
2573 wine_dbgstr_guid(context));
2575 if (!levels)
2576 return NULL_PTR_ERR;
2578 if (count != session->channel_count)
2579 return E_INVALIDARG;
2581 if (context)
2582 FIXME("Notifications not supported yet\n");
2584 TRACE("PulseAudio does not support session volume control\n");
2586 EnterCriticalSection(&session_cs);
2587 for(i = 0; i < count; ++i)
2588 session->channel_vols[i] = levels[i];
2589 LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry)
2590 set_stream_volumes(client);
2591 LeaveCriticalSection(&session_cs);
2592 return S_OK;
2595 static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes(
2596 IChannelAudioVolume *iface, UINT32 count, float *levels)
2598 AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface);
2599 AudioSession *session = This->session;
2600 int i;
2602 TRACE("(%p)->(%d, %p)\n", session, count, levels);
2604 if (!levels)
2605 return NULL_PTR_ERR;
2607 if (count != session->channel_count)
2608 return E_INVALIDARG;
2610 for(i = 0; i < count; ++i)
2611 levels[i] = session->channel_vols[i];
2613 return S_OK;
2616 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl =
2618 ChannelAudioVolume_QueryInterface,
2619 ChannelAudioVolume_AddRef,
2620 ChannelAudioVolume_Release,
2621 ChannelAudioVolume_GetChannelCount,
2622 ChannelAudioVolume_SetChannelVolume,
2623 ChannelAudioVolume_GetChannelVolume,
2624 ChannelAudioVolume_SetAllVolumes,
2625 ChannelAudioVolume_GetAllVolumes
2628 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
2629 IAudioSessionManager2 **out)
2631 SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr));
2632 *out = NULL;
2633 if (!This)
2634 return E_OUTOFMEMORY;
2635 This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl;
2636 This->device = device;
2637 This->ref = 1;
2638 *out = &This->IAudioSessionManager2_iface;
2639 return S_OK;
2642 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
2644 struct get_prop_value_params params;
2645 char pulse_name[MAX_PULSE_NAME_LEN];
2646 DWORD size;
2648 TRACE("%s, (%s,%u), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
2650 if (!get_pulse_name_by_guid(guid, pulse_name, &params.flow))
2651 return E_FAIL;
2653 params.pulse_name = pulse_name;
2654 params.guid = guid;
2655 params.prop = prop;
2656 pulse_call(get_prop_value, &params);
2658 if (params.result != S_OK)
2659 return params.result;
2661 switch (params.vt) {
2662 case VT_LPWSTR:
2663 size = (wcslen(params.wstr) + 1) * sizeof(WCHAR);
2664 if (!(out->pwszVal = CoTaskMemAlloc(size)))
2665 return E_OUTOFMEMORY;
2666 memcpy(out->pwszVal, params.wstr, size);
2667 break;
2668 case VT_UI4:
2669 out->ulVal = params.ulVal;
2670 break;
2671 default:
2672 assert(0);
2674 out->vt = params.vt;
2676 return S_OK;