gdiplus/tests: Add pen custom line cap record and play back tests.
[wine.git] / dlls / winepulse.drv / mmdevdrv.c
blob110f23e1319d66962300c2e53d3c7076429e438e
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"
51 #include "../mmdevapi/mmdevdrv.h"
53 WINE_DEFAULT_DEBUG_CHANNEL(pulse);
55 #define MAX_PULSE_NAME_LEN 256
57 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
59 static HANDLE pulse_thread;
60 static struct list g_sessions = LIST_INIT(g_sessions);
61 static struct list g_devices_cache = LIST_INIT(g_devices_cache);
63 struct device_cache {
64 struct list entry;
65 GUID guid;
66 EDataFlow dataflow;
67 char pulse_name[0];
70 static GUID pulse_render_guid =
71 { 0xfd47d9cc, 0x4218, 0x4135, { 0x9c, 0xe2, 0x0c, 0x19, 0x5c, 0x87, 0x40, 0x5b } };
72 static GUID pulse_capture_guid =
73 { 0x25da76d0, 0x033c, 0x4235, { 0x90, 0x02, 0x19, 0xf4, 0x88, 0x94, 0xac, 0x6f } };
75 static WCHAR drv_key_devicesW[256];
77 static CRITICAL_SECTION session_cs;
78 static CRITICAL_SECTION_DEBUG session_cs_debug = {
79 0, 0, &session_cs,
80 { &session_cs_debug.ProcessLocksList,
81 &session_cs_debug.ProcessLocksList },
82 0, 0, { (DWORD_PTR)(__FILE__ ": session_cs") }
84 static CRITICAL_SECTION session_cs = { &session_cs_debug, -1, 0, 0, 0, 0 };
86 void DECLSPEC_HIDDEN sessions_lock(void)
88 EnterCriticalSection(&session_cs);
91 void DECLSPEC_HIDDEN sessions_unlock(void)
93 LeaveCriticalSection(&session_cs);
96 BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved)
98 if (reason == DLL_PROCESS_ATTACH) {
99 WCHAR buf[MAX_PATH];
100 WCHAR *filename;
102 DisableThreadLibraryCalls(dll);
103 if (__wine_init_unix_call())
104 return FALSE;
106 GetModuleFileNameW(dll, buf, ARRAY_SIZE(buf));
108 filename = wcsrchr(buf, '\\');
109 filename = filename ? filename + 1 : buf;
111 swprintf(drv_key_devicesW, ARRAY_SIZE(drv_key_devicesW),
112 L"Software\\Wine\\Drivers\\%s\\devices", filename);
113 } else if (reason == DLL_PROCESS_DETACH) {
114 struct device_cache *device, *device_next;
116 LIST_FOR_EACH_ENTRY_SAFE(device, device_next, &g_devices_cache, struct device_cache, entry)
117 free(device);
119 if (pulse_thread) {
120 WaitForSingleObject(pulse_thread, INFINITE);
121 CloseHandle(pulse_thread);
124 return TRUE;
127 static const IAudioClient3Vtbl AudioClient3_Vtbl;
128 extern const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
129 extern const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
130 extern const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl;
131 extern const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl;
132 extern const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl;
133 extern const IAudioClockVtbl AudioClock_Vtbl;
134 extern const IAudioClock2Vtbl AudioClock2_Vtbl;
135 extern const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl;
137 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client);
139 static inline ACImpl *impl_from_IAudioClient3(IAudioClient3 *iface)
141 return CONTAINING_RECORD(iface, ACImpl, IAudioClient3_iface);
144 static void pulse_call(enum unix_funcs code, void *params)
146 NTSTATUS status;
147 status = WINE_UNIX_CALL(code, params);
148 assert(!status);
151 static void pulse_release_stream(stream_handle stream, HANDLE timer)
153 struct release_stream_params params;
154 params.stream = stream;
155 params.timer_thread = timer;
156 pulse_call(release_stream, &params);
159 static DWORD CALLBACK pulse_mainloop_thread(void *event)
161 struct main_loop_params params;
162 params.event = event;
163 SetThreadDescription(GetCurrentThread(), L"winepulse_mainloop");
164 pulse_call(main_loop, &params);
165 return 0;
168 typedef struct tagLANGANDCODEPAGE
170 WORD wLanguage;
171 WORD wCodePage;
172 } LANGANDCODEPAGE;
174 static BOOL query_productname(void *data, LANGANDCODEPAGE *lang, LPVOID *buffer, UINT *len)
176 WCHAR pn[37];
177 swprintf(pn, ARRAY_SIZE(pn), L"\\StringFileInfo\\%04x%04x\\ProductName", lang->wLanguage, lang->wCodePage);
178 return VerQueryValueW(data, pn, buffer, len) && *len;
181 static WCHAR *get_application_name(BOOL query_app_name)
183 WCHAR path[MAX_PATH], *name;
185 GetModuleFileNameW(NULL, path, ARRAY_SIZE(path));
187 if (query_app_name)
189 UINT translate_size, productname_size;
190 LANGANDCODEPAGE *translate;
191 LPVOID productname;
192 BOOL found = FALSE;
193 void *data = NULL;
194 unsigned int i;
195 LCID locale;
196 DWORD size;
198 size = GetFileVersionInfoSizeW(path, NULL);
199 if (!size)
200 goto skip;
202 data = malloc(size);
203 if (!data)
204 goto skip;
206 if (!GetFileVersionInfoW(path, 0, size, data))
207 goto skip;
209 if (!VerQueryValueW(data, L"\\VarFileInfo\\Translation", (LPVOID *)&translate, &translate_size))
210 goto skip;
212 /* no translations found */
213 if (translate_size < sizeof(LANGANDCODEPAGE))
214 goto skip;
216 /* The following code will try to find the best translation. We first search for an
217 * exact match of the language, then a match of the language PRIMARYLANGID, then we
218 * search for a LANG_NEUTRAL match, and if that still doesn't work we pick the
219 * first entry which contains a proper productname. */
220 locale = GetThreadLocale();
222 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
223 if (translate[i].wLanguage == locale &&
224 query_productname(data, &translate[i], &productname, &productname_size)) {
225 found = TRUE;
226 break;
230 if (!found) {
231 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
232 if (PRIMARYLANGID(translate[i].wLanguage) == PRIMARYLANGID(locale) &&
233 query_productname(data, &translate[i], &productname, &productname_size)) {
234 found = TRUE;
235 break;
240 if (!found) {
241 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
242 if (PRIMARYLANGID(translate[i].wLanguage) == LANG_NEUTRAL &&
243 query_productname(data, &translate[i], &productname, &productname_size)) {
244 found = TRUE;
245 break;
250 if (!found) {
251 for (i = 0; i < translate_size / sizeof(LANGANDCODEPAGE); i++) {
252 if (query_productname(data, &translate[i], &productname, &productname_size)) {
253 found = TRUE;
254 break;
259 skip:
260 if (found)
262 name = wcsdup(productname);
263 free(data);
264 return name;
266 free(data);
269 name = wcsrchr(path, '\\');
270 if (!name)
271 name = path;
272 else
273 name++;
274 return wcsdup(name);
277 static DWORD WINAPI pulse_timer_cb(void *user)
279 struct timer_loop_params params;
280 ACImpl *This = user;
281 params.stream = This->stream;
282 SetThreadDescription(GetCurrentThread(), L"winepulse_timer_loop");
283 pulse_call(timer_loop, &params);
284 return 0;
287 static void set_stream_volumes(ACImpl *This)
289 struct set_volumes_params params;
290 params.stream = This->stream;
291 params.master_volume = This->session->mute ? 0.0f : This->session->master_vol;
292 params.volumes = This->vols;
293 params.session_volumes = This->session->channel_vols;
294 pulse_call(set_volumes, &params);
297 static void get_device_guid(HKEY drv_key, EDataFlow flow, const char *pulse_name, GUID *guid)
299 WCHAR key_name[MAX_PULSE_NAME_LEN + 2];
300 DWORD type, size = sizeof(*guid);
301 LSTATUS status;
302 HKEY dev_key;
304 if (!pulse_name[0]) {
305 *guid = (flow == eRender) ? pulse_render_guid : pulse_capture_guid;
306 return;
309 if (!drv_key) {
310 CoCreateGuid(guid);
311 return;
314 key_name[0] = (flow == eRender) ? '0' : '1';
315 key_name[1] = ',';
316 MultiByteToWideChar(CP_UNIXCP, 0, pulse_name, -1, key_name + 2, ARRAY_SIZE(key_name) - 2);
318 status = RegCreateKeyExW(drv_key, key_name, 0, NULL, 0, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY,
319 NULL, &dev_key, NULL);
320 if (status != ERROR_SUCCESS) {
321 ERR("Failed to open registry key for device %s: %lu\n", pulse_name, status);
322 CoCreateGuid(guid);
323 return;
326 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE*)guid, &size);
327 if (status != ERROR_SUCCESS || type != REG_BINARY || size != sizeof(*guid)) {
328 CoCreateGuid(guid);
329 status = RegSetValueExW(dev_key, L"guid", 0, REG_BINARY, (BYTE*)guid, sizeof(*guid));
330 if (status != ERROR_SUCCESS)
331 ERR("Failed to store device GUID for %s to registry: %lu\n", pulse_name, status);
333 RegCloseKey(dev_key);
336 HRESULT WINAPI AUDDRV_GetEndpointIDs(EDataFlow flow, WCHAR ***ids_out, GUID **keys,
337 UINT *num, UINT *def_index)
339 struct get_endpoint_ids_params params;
340 GUID *guids = NULL;
341 WCHAR **ids = NULL;
342 unsigned int i = 0;
343 LSTATUS status;
344 HKEY drv_key;
346 TRACE("%d %p %p %p\n", flow, ids_out, num, def_index);
348 params.flow = flow;
349 params.size = MAX_PULSE_NAME_LEN * 4;
350 params.endpoints = NULL;
351 do {
352 HeapFree(GetProcessHeap(), 0, params.endpoints);
353 params.endpoints = HeapAlloc(GetProcessHeap(), 0, params.size);
354 pulse_call(get_endpoint_ids, &params);
355 } while(params.result == HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER));
357 if (FAILED(params.result))
358 goto end;
360 ids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*ids));
361 guids = HeapAlloc(GetProcessHeap(), 0, params.num * sizeof(*guids));
362 if (!ids || !guids) {
363 params.result = E_OUTOFMEMORY;
364 goto end;
367 status = RegCreateKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, NULL, 0,
368 KEY_WRITE | KEY_WOW64_64KEY, NULL, &drv_key, NULL);
369 if (status != ERROR_SUCCESS) {
370 ERR("Failed to open devices registry key: %lu\n", status);
371 drv_key = NULL;
374 for (i = 0; i < params.num; i++) {
375 WCHAR *name = (WCHAR *)((char *)params.endpoints + params.endpoints[i].name);
376 char *pulse_name = (char *)params.endpoints + params.endpoints[i].device;
377 unsigned int size = (wcslen(name) + 1) * sizeof(WCHAR);
379 if (!(ids[i] = HeapAlloc(GetProcessHeap(), 0, size))) {
380 params.result = E_OUTOFMEMORY;
381 break;
383 memcpy(ids[i], name, size);
384 get_device_guid(drv_key, flow, pulse_name, &guids[i]);
386 if (drv_key)
387 RegCloseKey(drv_key);
389 end:
390 HeapFree(GetProcessHeap(), 0, params.endpoints);
391 if (FAILED(params.result)) {
392 HeapFree(GetProcessHeap(), 0, guids);
393 while (i--) HeapFree(GetProcessHeap(), 0, ids[i]);
394 HeapFree(GetProcessHeap(), 0, ids);
395 } else {
396 *ids_out = ids;
397 *keys = guids;
398 *num = params.num;
399 *def_index = params.default_idx;
401 return params.result;
404 static BOOL get_pulse_name_by_guid(const GUID *guid, char pulse_name[MAX_PULSE_NAME_LEN], EDataFlow *flow)
406 struct device_cache *device;
407 WCHAR key_name[MAX_PULSE_NAME_LEN + 2];
408 DWORD key_name_size;
409 DWORD index = 0;
410 HKEY key;
412 /* Return empty string for default PulseAudio device */
413 pulse_name[0] = 0;
414 if (IsEqualGUID(guid, &pulse_render_guid)) {
415 *flow = eRender;
416 return TRUE;
417 } else if (IsEqualGUID(guid, &pulse_capture_guid)) {
418 *flow = eCapture;
419 return TRUE;
422 /* Check the cache first */
423 LIST_FOR_EACH_ENTRY(device, &g_devices_cache, struct device_cache, entry) {
424 if (!IsEqualGUID(guid, &device->guid))
425 continue;
426 *flow = device->dataflow;
427 strcpy(pulse_name, device->pulse_name);
428 return TRUE;
431 if (RegOpenKeyExW(HKEY_CURRENT_USER, drv_key_devicesW, 0, KEY_READ | KEY_WOW64_64KEY, &key) != ERROR_SUCCESS) {
432 WARN("No devices found in registry\n");
433 return FALSE;
436 for (;;) {
437 DWORD size, type;
438 LSTATUS status;
439 GUID reg_guid;
440 HKEY dev_key;
441 int len;
443 key_name_size = ARRAY_SIZE(key_name);
444 if (RegEnumKeyExW(key, index++, key_name, &key_name_size, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
445 break;
447 if (RegOpenKeyExW(key, key_name, 0, KEY_READ | KEY_WOW64_64KEY, &dev_key) != ERROR_SUCCESS) {
448 ERR("Couldn't open key: %s\n", wine_dbgstr_w(key_name));
449 continue;
452 size = sizeof(reg_guid);
453 status = RegQueryValueExW(dev_key, L"guid", 0, &type, (BYTE *)&reg_guid, &size);
454 RegCloseKey(dev_key);
456 if (status == ERROR_SUCCESS && type == REG_BINARY && size == sizeof(reg_guid) && IsEqualGUID(&reg_guid, guid)) {
457 RegCloseKey(key);
459 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name));
461 if (key_name[0] == '0')
462 *flow = eRender;
463 else if (key_name[0] == '1')
464 *flow = eCapture;
465 else {
466 WARN("Unknown device type: %c\n", key_name[0]);
467 return FALSE;
470 if (!(len = WideCharToMultiByte(CP_UNIXCP, 0, key_name + 2, -1, pulse_name, MAX_PULSE_NAME_LEN, NULL, NULL)))
471 return FALSE;
473 if ((device = malloc(FIELD_OFFSET(struct device_cache, pulse_name[len])))) {
474 device->guid = reg_guid;
475 device->dataflow = *flow;
476 strcpy(device->pulse_name, pulse_name);
477 list_add_tail(&g_devices_cache, &device->entry);
479 return TRUE;
483 RegCloseKey(key);
484 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid));
485 return FALSE;
488 HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient **out)
490 ACImpl *This;
491 char pulse_name[MAX_PULSE_NAME_LEN];
492 EDataFlow dataflow;
493 unsigned len;
494 HRESULT hr;
496 TRACE("%s %p %p\n", debugstr_guid(guid), dev, out);
498 if (!get_pulse_name_by_guid(guid, pulse_name, &dataflow))
499 return AUDCLNT_E_DEVICE_INVALIDATED;
501 *out = NULL;
503 len = strlen(pulse_name) + 1;
504 This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, FIELD_OFFSET(ACImpl, device_name[len]));
505 if (!This)
506 return E_OUTOFMEMORY;
508 This->IAudioClient3_iface.lpVtbl = &AudioClient3_Vtbl;
509 This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
510 This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
511 This->IAudioClock_iface.lpVtbl = &AudioClock_Vtbl;
512 This->IAudioClock2_iface.lpVtbl = &AudioClock2_Vtbl;
513 This->IAudioStreamVolume_iface.lpVtbl = &AudioStreamVolume_Vtbl;
514 This->dataflow = dataflow;
515 This->parent = dev;
516 memcpy(This->device_name, pulse_name, len);
518 hr = CoCreateFreeThreadedMarshaler((IUnknown*)&This->IAudioClient3_iface, &This->marshal);
519 if (FAILED(hr)) {
520 HeapFree(GetProcessHeap(), 0, This);
521 return hr;
523 IMMDevice_AddRef(This->parent);
525 *out = (IAudioClient *)&This->IAudioClient3_iface;
526 IAudioClient3_AddRef(&This->IAudioClient3_iface);
528 return S_OK;
531 static HRESULT WINAPI AudioClient_QueryInterface(IAudioClient3 *iface,
532 REFIID riid, void **ppv)
534 ACImpl *This = impl_from_IAudioClient3(iface);
536 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
538 if (!ppv)
539 return E_POINTER;
541 *ppv = NULL;
542 if (IsEqualIID(riid, &IID_IUnknown) ||
543 IsEqualIID(riid, &IID_IAudioClient) ||
544 IsEqualIID(riid, &IID_IAudioClient2) ||
545 IsEqualIID(riid, &IID_IAudioClient3))
546 *ppv = iface;
547 if (*ppv) {
548 IUnknown_AddRef((IUnknown*)*ppv);
549 return S_OK;
552 if (IsEqualIID(riid, &IID_IMarshal))
553 return IUnknown_QueryInterface(This->marshal, riid, ppv);
555 WARN("Unknown interface %s\n", debugstr_guid(riid));
556 return E_NOINTERFACE;
559 static ULONG WINAPI AudioClient_AddRef(IAudioClient3 *iface)
561 ACImpl *This = impl_from_IAudioClient3(iface);
562 ULONG ref;
563 ref = InterlockedIncrement(&This->ref);
564 TRACE("(%p) Refcount now %lu\n", This, ref);
565 return ref;
568 static ULONG WINAPI AudioClient_Release(IAudioClient3 *iface)
570 ACImpl *This = impl_from_IAudioClient3(iface);
571 ULONG ref;
572 ref = InterlockedDecrement(&This->ref);
573 TRACE("(%p) Refcount now %lu\n", This, ref);
574 if (!ref) {
575 if (This->stream) {
576 pulse_release_stream(This->stream, This->timer_thread);
577 This->stream = 0;
578 sessions_lock();
579 list_remove(&This->entry);
580 sessions_unlock();
582 IUnknown_Release(This->marshal);
583 IMMDevice_Release(This->parent);
584 HeapFree(GetProcessHeap(), 0, This);
586 return ref;
589 static void dump_fmt(const WAVEFORMATEX *fmt)
591 TRACE("wFormatTag: 0x%x (", fmt->wFormatTag);
592 switch(fmt->wFormatTag) {
593 case WAVE_FORMAT_PCM:
594 TRACE("WAVE_FORMAT_PCM");
595 break;
596 case WAVE_FORMAT_IEEE_FLOAT:
597 TRACE("WAVE_FORMAT_IEEE_FLOAT");
598 break;
599 case WAVE_FORMAT_EXTENSIBLE:
600 TRACE("WAVE_FORMAT_EXTENSIBLE");
601 break;
602 default:
603 TRACE("Unknown");
604 break;
606 TRACE(")\n");
608 TRACE("nChannels: %u\n", fmt->nChannels);
609 TRACE("nSamplesPerSec: %lu\n", fmt->nSamplesPerSec);
610 TRACE("nAvgBytesPerSec: %lu\n", fmt->nAvgBytesPerSec);
611 TRACE("nBlockAlign: %u\n", fmt->nBlockAlign);
612 TRACE("wBitsPerSample: %u\n", fmt->wBitsPerSample);
613 TRACE("cbSize: %u\n", fmt->cbSize);
615 if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
616 WAVEFORMATEXTENSIBLE *fmtex = (void*)fmt;
617 TRACE("dwChannelMask: %08lx\n", fmtex->dwChannelMask);
618 TRACE("Samples: %04x\n", fmtex->Samples.wReserved);
619 TRACE("SubFormat: %s\n", wine_dbgstr_guid(&fmtex->SubFormat));
623 static void session_init_vols(AudioSession *session, UINT channels)
625 if (session->channel_count < channels) {
626 UINT i;
628 if (session->channel_vols)
629 session->channel_vols = HeapReAlloc(GetProcessHeap(), 0,
630 session->channel_vols, sizeof(float) * channels);
631 else
632 session->channel_vols = HeapAlloc(GetProcessHeap(), 0,
633 sizeof(float) * channels);
634 if (!session->channel_vols)
635 return;
637 for(i = session->channel_count; i < channels; ++i)
638 session->channel_vols[i] = 1.f;
640 session->channel_count = channels;
644 static AudioSession *create_session(const GUID *guid, IMMDevice *device,
645 UINT num_channels)
647 AudioSession *ret;
649 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession));
650 if (!ret)
651 return NULL;
653 memcpy(&ret->guid, guid, sizeof(GUID));
655 ret->device = device;
657 list_init(&ret->clients);
659 list_add_head(&g_sessions, &ret->entry);
661 session_init_vols(ret, num_channels);
663 ret->master_vol = 1.f;
665 return ret;
668 /* if channels == 0, then this will return or create a session with
669 * matching dataflow and GUID. otherwise, channels must also match */
670 static HRESULT get_audio_session(const GUID *sessionguid,
671 IMMDevice *device, UINT channels, AudioSession **out)
673 AudioSession *session;
675 if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) {
676 *out = create_session(&GUID_NULL, device, channels);
677 if (!*out)
678 return E_OUTOFMEMORY;
680 return S_OK;
683 *out = NULL;
684 LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) {
685 if (session->device == device &&
686 IsEqualGUID(sessionguid, &session->guid)) {
687 session_init_vols(session, channels);
688 *out = session;
689 break;
693 if (!*out) {
694 *out = create_session(sessionguid, device, channels);
695 if (!*out)
696 return E_OUTOFMEMORY;
699 return S_OK;
702 static HRESULT WINAPI AudioClient_Initialize(IAudioClient3 *iface,
703 AUDCLNT_SHAREMODE mode, DWORD flags, REFERENCE_TIME duration,
704 REFERENCE_TIME period, const WAVEFORMATEX *fmt,
705 const GUID *sessionguid)
707 ACImpl *This = impl_from_IAudioClient3(iface);
708 struct create_stream_params params;
709 unsigned int i, channel_count;
710 stream_handle stream;
711 WCHAR *name;
712 HRESULT hr;
714 TRACE("(%p)->(%x, %lx, %s, %s, %p, %s)\n", This, mode, flags,
715 wine_dbgstr_longlong(duration), wine_dbgstr_longlong(period), fmt, debugstr_guid(sessionguid));
717 if (!fmt)
718 return E_POINTER;
719 dump_fmt(fmt);
721 if (mode != AUDCLNT_SHAREMODE_SHARED && mode != AUDCLNT_SHAREMODE_EXCLUSIVE)
722 return E_INVALIDARG;
723 if (mode == AUDCLNT_SHAREMODE_EXCLUSIVE)
724 return AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED;
726 if (flags & ~(AUDCLNT_STREAMFLAGS_CROSSPROCESS |
727 AUDCLNT_STREAMFLAGS_LOOPBACK |
728 AUDCLNT_STREAMFLAGS_EVENTCALLBACK |
729 AUDCLNT_STREAMFLAGS_NOPERSIST |
730 AUDCLNT_STREAMFLAGS_RATEADJUST |
731 AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED |
732 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE |
733 AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED |
734 AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY |
735 AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM)) {
736 FIXME("Unknown flags: %08lx\n", flags);
737 return E_INVALIDARG;
740 sessions_lock();
742 if (This->stream) {
743 sessions_unlock();
744 return AUDCLNT_E_ALREADY_INITIALIZED;
747 if (!pulse_thread)
749 HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL);
750 if (!(pulse_thread = CreateThread(NULL, 0, pulse_mainloop_thread, event, 0, NULL)))
752 ERR("Failed to create mainloop thread.\n");
753 sessions_unlock();
754 CloseHandle(event);
755 return E_FAIL;
757 SetThreadPriority(pulse_thread, THREAD_PRIORITY_TIME_CRITICAL);
758 WaitForSingleObject(event, INFINITE);
759 CloseHandle(event);
762 params.name = name = get_application_name(TRUE);
763 params.device = This->device_name;
764 params.flow = This->dataflow;
765 params.share = mode;
766 params.flags = flags;
767 params.duration = duration;
768 params.period = period;
769 params.fmt = fmt;
770 params.stream = &stream;
771 params.channel_count = &channel_count;
772 pulse_call(create_stream, &params);
773 free(name);
774 if (FAILED(hr = params.result))
776 sessions_unlock();
777 return hr;
780 if (!(This->vols = malloc(channel_count * sizeof(*This->vols))))
782 pulse_release_stream(stream, NULL);
783 sessions_unlock();
784 return E_OUTOFMEMORY;
786 for (i = 0; i < channel_count; i++)
787 This->vols[i] = 1.f;
789 hr = get_audio_session(sessionguid, This->parent, channel_count, &This->session);
790 if (FAILED(hr))
792 free(This->vols);
793 This->vols = NULL;
794 sessions_unlock();
795 pulse_release_stream(stream, NULL);
796 return E_OUTOFMEMORY;
799 This->stream = stream;
800 This->channel_count = channel_count;
801 list_add_tail(&This->session->clients, &This->entry);
802 set_stream_volumes(This);
804 sessions_unlock();
805 return S_OK;
808 static HRESULT WINAPI AudioClient_GetBufferSize(IAudioClient3 *iface,
809 UINT32 *out)
811 ACImpl *This = impl_from_IAudioClient3(iface);
812 struct get_buffer_size_params params;
814 TRACE("(%p)->(%p)\n", This, out);
816 if (!out)
817 return E_POINTER;
818 if (!This->stream)
819 return AUDCLNT_E_NOT_INITIALIZED;
821 params.stream = This->stream;
822 params.frames = out;
823 pulse_call(get_buffer_size, &params);
824 return params.result;
827 static HRESULT WINAPI AudioClient_GetStreamLatency(IAudioClient3 *iface,
828 REFERENCE_TIME *latency)
830 ACImpl *This = impl_from_IAudioClient3(iface);
831 struct get_latency_params params;
833 TRACE("(%p)->(%p)\n", This, latency);
835 if (!latency)
836 return E_POINTER;
837 if (!This->stream)
838 return AUDCLNT_E_NOT_INITIALIZED;
840 params.stream = This->stream;
841 params.latency = latency;
842 pulse_call(get_latency, &params);
843 return params.result;
846 static HRESULT WINAPI AudioClient_GetCurrentPadding(IAudioClient3 *iface,
847 UINT32 *out)
849 ACImpl *This = impl_from_IAudioClient3(iface);
850 struct get_current_padding_params params;
852 TRACE("(%p)->(%p)\n", This, out);
854 if (!out)
855 return E_POINTER;
856 if (!This->stream)
857 return AUDCLNT_E_NOT_INITIALIZED;
859 params.stream = This->stream;
860 params.padding = out;
861 pulse_call(get_current_padding, &params);
862 return params.result;
865 static HRESULT WINAPI AudioClient_IsFormatSupported(IAudioClient3 *iface,
866 AUDCLNT_SHAREMODE mode, const WAVEFORMATEX *fmt,
867 WAVEFORMATEX **out)
869 ACImpl *This = impl_from_IAudioClient3(iface);
870 struct is_format_supported_params params;
872 TRACE("(%p)->(%x, %p, %p)\n", This, mode, fmt, out);
874 if (fmt)
875 dump_fmt(fmt);
877 params.device = This->device_name;
878 params.flow = This->dataflow;
879 params.share = mode;
880 params.fmt_in = fmt;
881 params.fmt_out = NULL;
883 if (out) {
884 *out = NULL;
885 if (mode == AUDCLNT_SHAREMODE_SHARED)
886 params.fmt_out = CoTaskMemAlloc(sizeof(*params.fmt_out));
889 pulse_call(is_format_supported, &params);
891 if (params.result == S_FALSE)
892 *out = &params.fmt_out->Format;
893 else
894 CoTaskMemFree(params.fmt_out);
896 return params.result;
899 static HRESULT WINAPI AudioClient_GetMixFormat(IAudioClient3 *iface,
900 WAVEFORMATEX **pwfx)
902 ACImpl *This = impl_from_IAudioClient3(iface);
903 struct get_mix_format_params params;
905 TRACE("(%p)->(%p)\n", This, pwfx);
907 if (!pwfx)
908 return E_POINTER;
909 *pwfx = NULL;
911 params.device = This->device_name;
912 params.flow = This->dataflow;
913 params.fmt = CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
914 if (!params.fmt)
915 return E_OUTOFMEMORY;
917 pulse_call(get_mix_format, &params);
919 if (SUCCEEDED(params.result)) {
920 *pwfx = &params.fmt->Format;
921 dump_fmt(*pwfx);
922 } else {
923 CoTaskMemFree(params.fmt);
926 return params.result;
929 static HRESULT WINAPI AudioClient_GetDevicePeriod(IAudioClient3 *iface,
930 REFERENCE_TIME *defperiod, REFERENCE_TIME *minperiod)
932 struct get_device_period_params params;
933 ACImpl *This = impl_from_IAudioClient3(iface);
935 TRACE("(%p)->(%p, %p)\n", This, defperiod, minperiod);
937 if (!defperiod && !minperiod)
938 return E_POINTER;
940 params.flow = This->dataflow;
941 params.device = This->device_name;
942 params.def_period = defperiod;
943 params.min_period = minperiod;
945 pulse_call(get_device_period, &params);
947 return params.result;
950 static HRESULT WINAPI AudioClient_Start(IAudioClient3 *iface)
952 ACImpl *This = impl_from_IAudioClient3(iface);
953 struct start_params params;
954 HRESULT hr;
956 TRACE("(%p)\n", This);
958 if (!This->stream)
959 return AUDCLNT_E_NOT_INITIALIZED;
961 params.stream = This->stream;
962 pulse_call(start, &params);
963 if (FAILED(hr = params.result))
964 return hr;
966 if (!This->timer_thread) {
967 This->timer_thread = CreateThread(NULL, 0, pulse_timer_cb, This, 0, NULL);
968 SetThreadPriority(This->timer_thread, THREAD_PRIORITY_TIME_CRITICAL);
971 return S_OK;
974 static HRESULT WINAPI AudioClient_Stop(IAudioClient3 *iface)
976 ACImpl *This = impl_from_IAudioClient3(iface);
977 struct stop_params params;
979 TRACE("(%p)\n", This);
981 if (!This->stream)
982 return AUDCLNT_E_NOT_INITIALIZED;
984 params.stream = This->stream;
985 pulse_call(stop, &params);
986 return params.result;
989 static HRESULT WINAPI AudioClient_Reset(IAudioClient3 *iface)
991 ACImpl *This = impl_from_IAudioClient3(iface);
992 struct reset_params params;
994 TRACE("(%p)\n", This);
996 if (!This->stream)
997 return AUDCLNT_E_NOT_INITIALIZED;
999 params.stream = This->stream;
1000 pulse_call(reset, &params);
1001 return params.result;
1004 static HRESULT WINAPI AudioClient_SetEventHandle(IAudioClient3 *iface,
1005 HANDLE event)
1007 ACImpl *This = impl_from_IAudioClient3(iface);
1008 struct set_event_handle_params params;
1010 TRACE("(%p)->(%p)\n", This, event);
1012 if (!event)
1013 return E_INVALIDARG;
1014 if (!This->stream)
1015 return AUDCLNT_E_NOT_INITIALIZED;
1017 params.stream = This->stream;
1018 params.event = event;
1019 pulse_call(set_event_handle, &params);
1020 return params.result;
1023 static HRESULT WINAPI AudioClient_GetService(IAudioClient3 *iface, REFIID riid,
1024 void **ppv)
1026 ACImpl *This = impl_from_IAudioClient3(iface);
1028 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1030 if (!ppv)
1031 return E_POINTER;
1032 *ppv = NULL;
1034 if (!This->stream)
1035 return AUDCLNT_E_NOT_INITIALIZED;
1037 if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
1038 if (This->dataflow != eRender)
1039 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1040 *ppv = &This->IAudioRenderClient_iface;
1041 } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
1042 if (This->dataflow != eCapture)
1043 return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
1044 *ppv = &This->IAudioCaptureClient_iface;
1045 } else if (IsEqualIID(riid, &IID_IAudioClock)) {
1046 *ppv = &This->IAudioClock_iface;
1047 } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) {
1048 *ppv = &This->IAudioStreamVolume_iface;
1049 } else if (IsEqualIID(riid, &IID_IAudioSessionControl) ||
1050 IsEqualIID(riid, &IID_IChannelAudioVolume) ||
1051 IsEqualIID(riid, &IID_ISimpleAudioVolume)) {
1052 if (!This->session_wrapper) {
1053 This->session_wrapper = AudioSessionWrapper_Create(This);
1054 if (!This->session_wrapper)
1055 return E_OUTOFMEMORY;
1057 if (IsEqualIID(riid, &IID_IAudioSessionControl))
1058 *ppv = &This->session_wrapper->IAudioSessionControl2_iface;
1059 else if (IsEqualIID(riid, &IID_IChannelAudioVolume))
1060 *ppv = &This->session_wrapper->IChannelAudioVolume_iface;
1061 else if (IsEqualIID(riid, &IID_ISimpleAudioVolume))
1062 *ppv = &This->session_wrapper->ISimpleAudioVolume_iface;
1065 if (*ppv) {
1066 IUnknown_AddRef((IUnknown*)*ppv);
1067 return S_OK;
1070 FIXME("stub %s\n", debugstr_guid(riid));
1071 return E_NOINTERFACE;
1074 static HRESULT WINAPI AudioClient_IsOffloadCapable(IAudioClient3 *iface,
1075 AUDIO_STREAM_CATEGORY category, BOOL *offload_capable)
1077 ACImpl *This = impl_from_IAudioClient3(iface);
1079 TRACE("(%p)->(0x%x, %p)\n", This, category, offload_capable);
1081 if(!offload_capable)
1082 return E_INVALIDARG;
1084 *offload_capable = FALSE;
1086 return S_OK;
1089 static HRESULT WINAPI AudioClient_SetClientProperties(IAudioClient3 *iface,
1090 const AudioClientProperties *prop)
1092 ACImpl *This = impl_from_IAudioClient3(iface);
1093 const Win8AudioClientProperties *legacy_prop = (const Win8AudioClientProperties *)prop;
1095 TRACE("(%p)->(%p)\n", This, prop);
1097 if(!legacy_prop)
1098 return E_POINTER;
1100 if(legacy_prop->cbSize == sizeof(AudioClientProperties)){
1101 TRACE("{ bIsOffload: %u, eCategory: 0x%x, Options: 0x%x }\n",
1102 legacy_prop->bIsOffload,
1103 legacy_prop->eCategory,
1104 prop->Options);
1105 }else if(legacy_prop->cbSize == sizeof(Win8AudioClientProperties)){
1106 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
1107 legacy_prop->bIsOffload,
1108 legacy_prop->eCategory);
1109 }else{
1110 WARN("Unsupported Size = %d\n", legacy_prop->cbSize);
1111 return E_INVALIDARG;
1115 if(legacy_prop->bIsOffload)
1116 return AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE;
1118 return S_OK;
1121 static HRESULT WINAPI AudioClient_GetBufferSizeLimits(IAudioClient3 *iface,
1122 const WAVEFORMATEX *format, BOOL event_driven, REFERENCE_TIME *min_duration,
1123 REFERENCE_TIME *max_duration)
1125 ACImpl *This = impl_from_IAudioClient3(iface);
1127 FIXME("(%p)->(%p, %u, %p, %p)\n", This, format, event_driven, min_duration, max_duration);
1129 return E_NOTIMPL;
1132 static HRESULT WINAPI AudioClient_GetSharedModeEnginePeriod(IAudioClient3 *iface,
1133 const WAVEFORMATEX *format, UINT32 *default_period_frames, UINT32 *unit_period_frames,
1134 UINT32 *min_period_frames, UINT32 *max_period_frames)
1136 ACImpl *This = impl_from_IAudioClient3(iface);
1138 FIXME("(%p)->(%p, %p, %p, %p, %p)\n", This, format, default_period_frames, unit_period_frames,
1139 min_period_frames, max_period_frames);
1141 return E_NOTIMPL;
1144 static HRESULT WINAPI AudioClient_GetCurrentSharedModeEnginePeriod(IAudioClient3 *iface,
1145 WAVEFORMATEX **cur_format, UINT32 *cur_period_frames)
1147 ACImpl *This = impl_from_IAudioClient3(iface);
1149 FIXME("(%p)->(%p, %p)\n", This, cur_format, cur_period_frames);
1151 return E_NOTIMPL;
1154 static HRESULT WINAPI AudioClient_InitializeSharedAudioStream(IAudioClient3 *iface,
1155 DWORD flags, UINT32 period_frames, const WAVEFORMATEX *format,
1156 const GUID *session_guid)
1158 ACImpl *This = impl_from_IAudioClient3(iface);
1160 FIXME("(%p)->(0x%lx, %u, %p, %s)\n", This, flags, period_frames, format, debugstr_guid(session_guid));
1162 return E_NOTIMPL;
1165 static const IAudioClient3Vtbl AudioClient3_Vtbl =
1167 AudioClient_QueryInterface,
1168 AudioClient_AddRef,
1169 AudioClient_Release,
1170 AudioClient_Initialize,
1171 AudioClient_GetBufferSize,
1172 AudioClient_GetStreamLatency,
1173 AudioClient_GetCurrentPadding,
1174 AudioClient_IsFormatSupported,
1175 AudioClient_GetMixFormat,
1176 AudioClient_GetDevicePeriod,
1177 AudioClient_Start,
1178 AudioClient_Stop,
1179 AudioClient_Reset,
1180 AudioClient_SetEventHandle,
1181 AudioClient_GetService,
1182 AudioClient_IsOffloadCapable,
1183 AudioClient_SetClientProperties,
1184 AudioClient_GetBufferSizeLimits,
1185 AudioClient_GetSharedModeEnginePeriod,
1186 AudioClient_GetCurrentSharedModeEnginePeriod,
1187 AudioClient_InitializeSharedAudioStream,
1190 static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client)
1192 AudioSessionWrapper *ret;
1194 ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
1195 sizeof(AudioSessionWrapper));
1196 if (!ret)
1197 return NULL;
1199 ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl;
1200 ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl;
1201 ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl;
1203 ret->ref = !client;
1205 ret->client = client;
1206 if (client) {
1207 ret->session = client->session;
1208 IAudioClient3_AddRef(&client->IAudioClient3_iface);
1211 return ret;
1214 HRESULT WINAPI AUDDRV_GetAudioSessionWrapper(const GUID *guid, IMMDevice *device,
1215 AudioSessionWrapper **out)
1217 AudioSession *session;
1219 HRESULT hr = get_audio_session(guid, device, 0, &session);
1220 if(FAILED(hr))
1221 return hr;
1223 *out = AudioSessionWrapper_Create(NULL);
1224 if(!*out)
1225 return E_OUTOFMEMORY;
1227 (*out)->session = session;
1229 return S_OK;
1232 HRESULT WINAPI AUDDRV_GetPropValue(GUID *guid, const PROPERTYKEY *prop, PROPVARIANT *out)
1234 struct get_prop_value_params params;
1235 char pulse_name[MAX_PULSE_NAME_LEN];
1236 unsigned int size = 0;
1238 TRACE("%s, (%s,%lu), %p\n", wine_dbgstr_guid(guid), wine_dbgstr_guid(&prop->fmtid), prop->pid, out);
1240 if (!get_pulse_name_by_guid(guid, pulse_name, &params.flow))
1241 return E_FAIL;
1243 params.device = pulse_name;
1244 params.guid = guid;
1245 params.prop = prop;
1246 params.value = out;
1247 params.buffer = NULL;
1248 params.buffer_size = &size;
1250 while(1) {
1251 pulse_call(get_prop_value, &params);
1253 if(params.result != E_NOT_SUFFICIENT_BUFFER)
1254 break;
1256 CoTaskMemFree(params.buffer);
1257 params.buffer = CoTaskMemAlloc(*params.buffer_size);
1258 if(!params.buffer)
1259 return E_OUTOFMEMORY;
1261 if(FAILED(params.result))
1262 CoTaskMemFree(params.buffer);
1264 return params.result;