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
31 #include "wine/debug.h"
32 #include "wine/list.h"
33 #include "wine/unixlib.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
);
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
= {
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
) {
102 DisableThreadLibraryCalls(dll
);
103 if (__wine_init_unix_call())
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
)
120 WaitForSingleObject(pulse_thread
, INFINITE
);
121 CloseHandle(pulse_thread
);
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
)
147 status
= WINE_UNIX_CALL(code
, params
);
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
, ¶ms
);
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
, ¶ms
);
168 typedef struct tagLANGANDCODEPAGE
174 static BOOL
query_productname(void *data
, LANGANDCODEPAGE
*lang
, LPVOID
*buffer
, UINT
*len
)
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
));
189 UINT translate_size
, productname_size
;
190 LANGANDCODEPAGE
*translate
;
198 size
= GetFileVersionInfoSizeW(path
, NULL
);
206 if (!GetFileVersionInfoW(path
, 0, size
, data
))
209 if (!VerQueryValueW(data
, L
"\\VarFileInfo\\Translation", (LPVOID
*)&translate
, &translate_size
))
212 /* no translations found */
213 if (translate_size
< sizeof(LANGANDCODEPAGE
))
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
)) {
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
)) {
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
)) {
251 for (i
= 0; i
< translate_size
/ sizeof(LANGANDCODEPAGE
); i
++) {
252 if (query_productname(data
, &translate
[i
], &productname
, &productname_size
)) {
262 name
= wcsdup(productname
);
269 name
= wcsrchr(path
, '\\');
277 static DWORD WINAPI
pulse_timer_cb(void *user
)
279 struct timer_loop_params params
;
281 params
.stream
= This
->stream
;
282 SetThreadDescription(GetCurrentThread(), L
"winepulse_timer_loop");
283 pulse_call(timer_loop
, ¶ms
);
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
, ¶ms
);
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
);
304 if (!pulse_name
[0]) {
305 *guid
= (flow
== eRender
) ? pulse_render_guid
: pulse_capture_guid
;
314 key_name
[0] = (flow
== eRender
) ? '0' : '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
);
326 status
= RegQueryValueExW(dev_key
, L
"guid", 0, &type
, (BYTE
*)guid
, &size
);
327 if (status
!= ERROR_SUCCESS
|| type
!= REG_BINARY
|| size
!= sizeof(*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
;
346 TRACE("%d %p %p %p\n", flow
, ids_out
, num
, def_index
);
349 params
.size
= MAX_PULSE_NAME_LEN
* 4;
350 params
.endpoints
= NULL
;
352 HeapFree(GetProcessHeap(), 0, params
.endpoints
);
353 params
.endpoints
= HeapAlloc(GetProcessHeap(), 0, params
.size
);
354 pulse_call(get_endpoint_ids
, ¶ms
);
355 } while(params
.result
== HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER
));
357 if (FAILED(params
.result
))
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
;
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
);
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
;
383 memcpy(ids
[i
], name
, size
);
384 get_device_guid(drv_key
, flow
, pulse_name
, &guids
[i
]);
387 RegCloseKey(drv_key
);
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
);
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];
412 /* Return empty string for default PulseAudio device */
414 if (IsEqualGUID(guid
, &pulse_render_guid
)) {
417 } else if (IsEqualGUID(guid
, &pulse_capture_guid
)) {
422 /* Check the cache first */
423 LIST_FOR_EACH_ENTRY(device
, &g_devices_cache
, struct device_cache
, entry
) {
424 if (!IsEqualGUID(guid
, &device
->guid
))
426 *flow
= device
->dataflow
;
427 strcpy(pulse_name
, device
->pulse_name
);
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");
443 key_name_size
= ARRAY_SIZE(key_name
);
444 if (RegEnumKeyExW(key
, index
++, key_name
, &key_name_size
, NULL
, NULL
, NULL
, NULL
) != ERROR_SUCCESS
)
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
));
452 size
= sizeof(reg_guid
);
453 status
= RegQueryValueExW(dev_key
, L
"guid", 0, &type
, (BYTE
*)®_guid
, &size
);
454 RegCloseKey(dev_key
);
456 if (status
== ERROR_SUCCESS
&& type
== REG_BINARY
&& size
== sizeof(reg_guid
) && IsEqualGUID(®_guid
, guid
)) {
459 TRACE("Found matching device key: %s\n", wine_dbgstr_w(key_name
));
461 if (key_name
[0] == '0')
463 else if (key_name
[0] == '1')
466 WARN("Unknown device type: %c\n", key_name
[0]);
470 if (!(len
= WideCharToMultiByte(CP_UNIXCP
, 0, key_name
+ 2, -1, pulse_name
, MAX_PULSE_NAME_LEN
, NULL
, NULL
)))
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
);
484 WARN("No matching device in registry for GUID %s\n", debugstr_guid(guid
));
488 HRESULT WINAPI
AUDDRV_GetAudioEndpoint(GUID
*guid
, IMMDevice
*dev
, IAudioClient
**out
)
491 char pulse_name
[MAX_PULSE_NAME_LEN
];
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
;
503 len
= strlen(pulse_name
) + 1;
504 This
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, FIELD_OFFSET(ACImpl
, device_name
[len
]));
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
;
516 memcpy(This
->device_name
, pulse_name
, len
);
518 hr
= CoCreateFreeThreadedMarshaler((IUnknown
*)&This
->IAudioClient3_iface
, &This
->marshal
);
520 HeapFree(GetProcessHeap(), 0, This
);
523 IMMDevice_AddRef(This
->parent
);
525 *out
= (IAudioClient
*)&This
->IAudioClient3_iface
;
526 IAudioClient3_AddRef(&This
->IAudioClient3_iface
);
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
);
542 if (IsEqualIID(riid
, &IID_IUnknown
) ||
543 IsEqualIID(riid
, &IID_IAudioClient
) ||
544 IsEqualIID(riid
, &IID_IAudioClient2
) ||
545 IsEqualIID(riid
, &IID_IAudioClient3
))
548 IUnknown_AddRef((IUnknown
*)*ppv
);
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
);
563 ref
= InterlockedIncrement(&This
->ref
);
564 TRACE("(%p) Refcount now %lu\n", This
, ref
);
568 static ULONG WINAPI
AudioClient_Release(IAudioClient3
*iface
)
570 ACImpl
*This
= impl_from_IAudioClient3(iface
);
572 ref
= InterlockedDecrement(&This
->ref
);
573 TRACE("(%p) Refcount now %lu\n", This
, ref
);
576 pulse_release_stream(This
->stream
, This
->timer_thread
);
579 list_remove(&This
->entry
);
582 IUnknown_Release(This
->marshal
);
583 IMMDevice_Release(This
->parent
);
584 HeapFree(GetProcessHeap(), 0, This
);
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");
596 case WAVE_FORMAT_IEEE_FLOAT
:
597 TRACE("WAVE_FORMAT_IEEE_FLOAT");
599 case WAVE_FORMAT_EXTENSIBLE
:
600 TRACE("WAVE_FORMAT_EXTENSIBLE");
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
) {
628 if (session
->channel_vols
)
629 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0,
630 session
->channel_vols
, sizeof(float) * channels
);
632 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0,
633 sizeof(float) * channels
);
634 if (!session
->channel_vols
)
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
,
649 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(AudioSession
));
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
;
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
);
678 return E_OUTOFMEMORY
;
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
);
694 *out
= create_session(sessionguid
, device
, channels
);
696 return E_OUTOFMEMORY
;
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
;
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
));
721 if (mode
!= AUDCLNT_SHAREMODE_SHARED
&& mode
!= AUDCLNT_SHAREMODE_EXCLUSIVE
)
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
);
744 return AUDCLNT_E_ALREADY_INITIALIZED
;
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");
757 SetThreadPriority(pulse_thread
, THREAD_PRIORITY_TIME_CRITICAL
);
758 WaitForSingleObject(event
, INFINITE
);
762 params
.name
= name
= get_application_name(TRUE
);
763 params
.device
= This
->device_name
;
764 params
.flow
= This
->dataflow
;
766 params
.flags
= flags
;
767 params
.duration
= duration
;
768 params
.period
= period
;
770 params
.stream
= &stream
;
771 params
.channel_count
= &channel_count
;
772 pulse_call(create_stream
, ¶ms
);
774 if (FAILED(hr
= params
.result
))
780 if (!(This
->vols
= malloc(channel_count
* sizeof(*This
->vols
))))
782 pulse_release_stream(stream
, NULL
);
784 return E_OUTOFMEMORY
;
786 for (i
= 0; i
< channel_count
; i
++)
789 hr
= get_audio_session(sessionguid
, This
->parent
, channel_count
, &This
->session
);
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
);
808 static HRESULT WINAPI
AudioClient_GetBufferSize(IAudioClient3
*iface
,
811 ACImpl
*This
= impl_from_IAudioClient3(iface
);
812 struct get_buffer_size_params params
;
814 TRACE("(%p)->(%p)\n", This
, out
);
819 return AUDCLNT_E_NOT_INITIALIZED
;
821 params
.stream
= This
->stream
;
823 pulse_call(get_buffer_size
, ¶ms
);
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
);
838 return AUDCLNT_E_NOT_INITIALIZED
;
840 params
.stream
= This
->stream
;
841 params
.latency
= latency
;
842 pulse_call(get_latency
, ¶ms
);
843 return params
.result
;
846 static HRESULT WINAPI
AudioClient_GetCurrentPadding(IAudioClient3
*iface
,
849 ACImpl
*This
= impl_from_IAudioClient3(iface
);
850 struct get_current_padding_params params
;
852 TRACE("(%p)->(%p)\n", This
, out
);
857 return AUDCLNT_E_NOT_INITIALIZED
;
859 params
.stream
= This
->stream
;
860 params
.padding
= out
;
861 pulse_call(get_current_padding
, ¶ms
);
862 return params
.result
;
865 static HRESULT WINAPI
AudioClient_IsFormatSupported(IAudioClient3
*iface
,
866 AUDCLNT_SHAREMODE mode
, const WAVEFORMATEX
*fmt
,
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
);
877 params
.device
= This
->device_name
;
878 params
.flow
= This
->dataflow
;
881 params
.fmt_out
= NULL
;
885 if (mode
== AUDCLNT_SHAREMODE_SHARED
)
886 params
.fmt_out
= CoTaskMemAlloc(sizeof(*params
.fmt_out
));
889 pulse_call(is_format_supported
, ¶ms
);
891 if (params
.result
== S_FALSE
)
892 *out
= ¶ms
.fmt_out
->Format
;
894 CoTaskMemFree(params
.fmt_out
);
896 return params
.result
;
899 static HRESULT WINAPI
AudioClient_GetMixFormat(IAudioClient3
*iface
,
902 ACImpl
*This
= impl_from_IAudioClient3(iface
);
903 struct get_mix_format_params params
;
905 TRACE("(%p)->(%p)\n", This
, pwfx
);
911 params
.device
= This
->device_name
;
912 params
.flow
= This
->dataflow
;
913 params
.fmt
= CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE
));
915 return E_OUTOFMEMORY
;
917 pulse_call(get_mix_format
, ¶ms
);
919 if (SUCCEEDED(params
.result
)) {
920 *pwfx
= ¶ms
.fmt
->Format
;
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
)
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
, ¶ms
);
947 return params
.result
;
950 static HRESULT WINAPI
AudioClient_Start(IAudioClient3
*iface
)
952 ACImpl
*This
= impl_from_IAudioClient3(iface
);
953 struct start_params params
;
956 TRACE("(%p)\n", This
);
959 return AUDCLNT_E_NOT_INITIALIZED
;
961 params
.stream
= This
->stream
;
962 pulse_call(start
, ¶ms
);
963 if (FAILED(hr
= params
.result
))
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
);
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
);
982 return AUDCLNT_E_NOT_INITIALIZED
;
984 params
.stream
= This
->stream
;
985 pulse_call(stop
, ¶ms
);
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
);
997 return AUDCLNT_E_NOT_INITIALIZED
;
999 params
.stream
= This
->stream
;
1000 pulse_call(reset
, ¶ms
);
1001 return params
.result
;
1004 static HRESULT WINAPI
AudioClient_SetEventHandle(IAudioClient3
*iface
,
1007 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1008 struct set_event_handle_params params
;
1010 TRACE("(%p)->(%p)\n", This
, event
);
1013 return E_INVALIDARG
;
1015 return AUDCLNT_E_NOT_INITIALIZED
;
1017 params
.stream
= This
->stream
;
1018 params
.event
= event
;
1019 pulse_call(set_event_handle
, ¶ms
);
1020 return params
.result
;
1023 static HRESULT WINAPI
AudioClient_GetService(IAudioClient3
*iface
, REFIID riid
,
1026 ACImpl
*This
= impl_from_IAudioClient3(iface
);
1028 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), ppv
);
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
;
1066 IUnknown_AddRef((IUnknown
*)*ppv
);
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
;
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
);
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
,
1105 }else if(legacy_prop
->cbSize
== sizeof(Win8AudioClientProperties
)){
1106 TRACE("{ bIsOffload: %u, eCategory: 0x%x }\n",
1107 legacy_prop
->bIsOffload
,
1108 legacy_prop
->eCategory
);
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
;
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
);
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
);
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
);
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
));
1165 static const IAudioClient3Vtbl AudioClient3_Vtbl
=
1167 AudioClient_QueryInterface
,
1169 AudioClient_Release
,
1170 AudioClient_Initialize
,
1171 AudioClient_GetBufferSize
,
1172 AudioClient_GetStreamLatency
,
1173 AudioClient_GetCurrentPadding
,
1174 AudioClient_IsFormatSupported
,
1175 AudioClient_GetMixFormat
,
1176 AudioClient_GetDevicePeriod
,
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
));
1199 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
1200 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
1201 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
1205 ret
->client
= client
;
1207 ret
->session
= client
->session
;
1208 IAudioClient3_AddRef(&client
->IAudioClient3_iface
);
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
);
1223 *out
= AudioSessionWrapper_Create(NULL
);
1225 return E_OUTOFMEMORY
;
1227 (*out
)->session
= session
;
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
, ¶ms
.flow
))
1243 params
.device
= pulse_name
;
1247 params
.buffer
= NULL
;
1248 params
.buffer_size
= &size
;
1251 pulse_call(get_prop_value
, ¶ms
);
1253 if(params
.result
!= E_NOT_SUFFICIENT_BUFFER
)
1256 CoTaskMemFree(params
.buffer
);
1257 params
.buffer
= CoTaskMemAlloc(*params
.buffer_size
);
1259 return E_OUTOFMEMORY
;
1261 if(FAILED(params
.result
))
1262 CoTaskMemFree(params
.buffer
);
1264 return params
.result
;