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
24 #include <audiopolicy.h>
25 #include <mmdeviceapi.h>
28 #include <wine/debug.h>
29 #include <wine/unixlib.h>
31 #include "mmdevapi_private.h"
33 #define NULL_PTR_ERR MAKE_HRESULT(SEVERITY_ERROR, FACILITY_WIN32, RPC_X_NULL_REF_POINTER)
35 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
37 extern void sessions_lock(void) DECLSPEC_HIDDEN
;
38 extern void sessions_unlock(void) DECLSPEC_HIDDEN
;
40 extern void set_stream_volumes(struct audio_client
*This
) DECLSPEC_HIDDEN
;
42 static struct list sessions
= LIST_INIT(sessions
);
44 static inline struct audio_session_wrapper
*impl_from_IAudioSessionControl2(IAudioSessionControl2
*iface
)
46 return CONTAINING_RECORD(iface
, struct audio_session_wrapper
, IAudioSessionControl2_iface
);
49 static inline struct audio_session_wrapper
*impl_from_IChannelAudioVolume(IChannelAudioVolume
*iface
)
51 return CONTAINING_RECORD(iface
, struct audio_session_wrapper
, IChannelAudioVolume_iface
);
54 static inline struct audio_session_wrapper
*impl_from_ISimpleAudioVolume(ISimpleAudioVolume
*iface
)
56 return CONTAINING_RECORD(iface
, struct audio_session_wrapper
, ISimpleAudioVolume_iface
);
59 static HRESULT WINAPI
control_QueryInterface(IAudioSessionControl2
*iface
, REFIID riid
, void **ppv
)
61 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
66 if (IsEqualIID(riid
, &IID_IUnknown
) ||
67 IsEqualIID(riid
, &IID_IAudioSessionControl
) ||
68 IsEqualIID(riid
, &IID_IAudioSessionControl2
))
75 IUnknown_AddRef((IUnknown
*)*ppv
);
80 static ULONG WINAPI
control_AddRef(IAudioSessionControl2
*iface
)
82 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
83 ULONG ref
= InterlockedIncrement(&This
->ref
);
84 TRACE("(%p) Refcount now %lu\n", This
, ref
);
88 static ULONG WINAPI
control_Release(IAudioSessionControl2
*iface
)
90 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
91 ULONG ref
= InterlockedDecrement(&This
->ref
);
92 TRACE("(%p) Refcount now %lu\n", This
, ref
);
97 This
->client
->session_wrapper
= NULL
;
99 IAudioClient3_Release(&This
->client
->IAudioClient3_iface
);
102 HeapFree(GetProcessHeap(), 0, This
);
108 static HRESULT WINAPI
control_GetState(IAudioSessionControl2
*iface
, AudioSessionState
*state
)
110 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
111 struct is_started_params params
;
112 struct audio_client
*client
;
114 TRACE("(%p)->(%p)\n", This
, state
);
121 if (list_empty(&This
->session
->clients
)) {
122 *state
= AudioSessionStateExpired
;
127 LIST_FOR_EACH_ENTRY(client
, &This
->session
->clients
, struct audio_client
, entry
) {
128 params
.stream
= client
->stream
;
129 wine_unix_call(is_started
, ¶ms
);
130 if (params
.result
== S_OK
) {
131 *state
= AudioSessionStateActive
;
139 *state
= AudioSessionStateInactive
;
144 static HRESULT WINAPI
control_GetDisplayName(IAudioSessionControl2
*iface
, WCHAR
**name
)
146 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
147 FIXME("(%p)->(%p) - stub\n", This
, name
);
151 static HRESULT WINAPI
control_SetDisplayName(IAudioSessionControl2
*iface
, const WCHAR
*name
,
154 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
155 FIXME("(%p)->(%p, %s) - stub\n", This
, name
, debugstr_guid(session
));
159 static HRESULT WINAPI
control_GetIconPath(IAudioSessionControl2
*iface
, WCHAR
**path
)
161 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
162 FIXME("(%p)->(%p) - stub\n", This
, path
);
166 static HRESULT WINAPI
control_SetIconPath(IAudioSessionControl2
*iface
, const WCHAR
*path
,
169 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
170 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_w(path
), debugstr_guid(session
));
174 static HRESULT WINAPI
control_GetGroupingParam(IAudioSessionControl2
*iface
, GUID
*group
)
176 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
177 FIXME("(%p)->(%p) - stub\n", This
, group
);
181 static HRESULT WINAPI
control_SetGroupingParam(IAudioSessionControl2
*iface
, const GUID
*group
,
184 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
185 FIXME("(%p)->(%s, %s) - stub\n", This
, debugstr_guid(group
), debugstr_guid(session
));
189 static HRESULT WINAPI
control_RegisterAudioSessionNotification(IAudioSessionControl2
*iface
,
190 IAudioSessionEvents
*events
)
192 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
193 FIXME("(%p)->(%p) - stub\n", This
, events
);
197 static HRESULT WINAPI
control_UnregisterAudioSessionNotification(IAudioSessionControl2
*iface
,
198 IAudioSessionEvents
*events
)
200 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
201 FIXME("(%p)->(%p) - stub\n", This
, events
);
205 static HRESULT WINAPI
control_GetSessionIdentifier(IAudioSessionControl2
*iface
, WCHAR
**id
)
207 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
208 FIXME("(%p)->(%p) - stub\n", This
, id
);
212 static HRESULT WINAPI
control_GetSessionInstanceIdentifier(IAudioSessionControl2
*iface
, WCHAR
**id
)
214 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
215 FIXME("(%p)->(%p) - stub\n", This
, id
);
219 static HRESULT WINAPI
control_GetProcessId(IAudioSessionControl2
*iface
, DWORD
*pid
)
221 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
223 TRACE("(%p)->(%p)\n", This
, pid
);
228 *pid
= GetCurrentProcessId();
233 static HRESULT WINAPI
control_IsSystemSoundsSession(IAudioSessionControl2
*iface
)
235 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
236 TRACE("(%p)\n", This
);
240 static HRESULT WINAPI
control_SetDuckingPreference(IAudioSessionControl2
*iface
, BOOL optout
)
242 struct audio_session_wrapper
*This
= impl_from_IAudioSessionControl2(iface
);
243 TRACE("(%p)->(%d)\n", This
, optout
);
247 static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl
=
249 control_QueryInterface
,
253 control_GetDisplayName
,
254 control_SetDisplayName
,
257 control_GetGroupingParam
,
258 control_SetGroupingParam
,
259 control_RegisterAudioSessionNotification
,
260 control_UnregisterAudioSessionNotification
,
261 control_GetSessionIdentifier
,
262 control_GetSessionInstanceIdentifier
,
263 control_GetProcessId
,
264 control_IsSystemSoundsSession
,
265 control_SetDuckingPreference
268 static HRESULT WINAPI
channelvolume_QueryInterface(IChannelAudioVolume
*iface
, REFIID riid
,
271 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
276 if (IsEqualIID(riid
, &IID_IUnknown
) ||
277 IsEqualIID(riid
, &IID_IChannelAudioVolume
))
281 return E_NOINTERFACE
;
284 IUnknown_AddRef((IUnknown
*)*ppv
);
289 static ULONG WINAPI
channelvolume_AddRef(IChannelAudioVolume
*iface
)
291 struct audio_session_wrapper
*This
= impl_from_IChannelAudioVolume(iface
);
292 return IAudioSessionControl2_AddRef(&This
->IAudioSessionControl2_iface
);
295 static ULONG WINAPI
channelvolume_Release(IChannelAudioVolume
*iface
)
297 struct audio_session_wrapper
*This
= impl_from_IChannelAudioVolume(iface
);
298 return IAudioSessionControl2_Release(&This
->IAudioSessionControl2_iface
);
301 static HRESULT WINAPI
channelvolume_GetChannelCount(IChannelAudioVolume
*iface
, UINT32
*out
)
303 struct audio_session_wrapper
*This
= impl_from_IChannelAudioVolume(iface
);
304 struct audio_session
*session
= This
->session
;
306 TRACE("(%p)->(%p)\n", session
, out
);
311 *out
= session
->channel_count
;
316 static HRESULT WINAPI
channelvolume_SetChannelVolume(IChannelAudioVolume
*iface
, UINT32 index
,
317 float level
, const GUID
*context
)
319 struct audio_session_wrapper
*This
= impl_from_IChannelAudioVolume(iface
);
320 struct audio_session
*session
= This
->session
;
321 struct audio_client
*client
;
323 TRACE("(%p)->(%d, %f, %s)\n", session
, index
, level
, wine_dbgstr_guid(context
));
325 if (level
< 0.f
|| level
> 1.f
)
328 if (index
>= session
->channel_count
)
332 FIXME("Notifications not supported yet\n");
336 session
->channel_vols
[index
] = level
;
338 LIST_FOR_EACH_ENTRY(client
, &session
->clients
, struct audio_client
, entry
)
339 set_stream_volumes(client
);
346 static HRESULT WINAPI
channelvolume_GetChannelVolume(IChannelAudioVolume
*iface
, UINT32 index
,
349 struct audio_session_wrapper
*This
= impl_from_IChannelAudioVolume(iface
);
350 struct audio_session
*session
= This
->session
;
352 TRACE("(%p)->(%d, %p)\n", session
, index
, level
);
357 if (index
>= session
->channel_count
)
360 *level
= session
->channel_vols
[index
];
365 static HRESULT WINAPI
channelvolume_SetAllVolumes(IChannelAudioVolume
*iface
, UINT32 count
,
366 const float *levels
, const GUID
*context
)
368 struct audio_session_wrapper
*This
= impl_from_IChannelAudioVolume(iface
);
369 struct audio_session
*session
= This
->session
;
370 struct audio_client
*client
;
373 TRACE("(%p)->(%d, %p, %s)\n", session
, count
, levels
, wine_dbgstr_guid(context
));
378 if (count
!= session
->channel_count
)
382 FIXME("Notifications not supported yet\n");
386 for (i
= 0; i
< count
; ++i
)
387 session
->channel_vols
[i
] = levels
[i
];
389 LIST_FOR_EACH_ENTRY(client
, &session
->clients
, struct audio_client
, entry
)
390 set_stream_volumes(client
);
397 static HRESULT WINAPI
channelvolume_GetAllVolumes(IChannelAudioVolume
*iface
, UINT32 count
,
400 struct audio_session_wrapper
*This
= impl_from_IChannelAudioVolume(iface
);
401 struct audio_session
*session
= This
->session
;
404 TRACE("(%p)->(%d, %p)\n", session
, count
, levels
);
409 if (count
!= session
->channel_count
)
412 for (i
= 0; i
< count
; ++i
)
413 levels
[i
] = session
->channel_vols
[i
];
418 static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl
=
420 channelvolume_QueryInterface
,
421 channelvolume_AddRef
,
422 channelvolume_Release
,
423 channelvolume_GetChannelCount
,
424 channelvolume_SetChannelVolume
,
425 channelvolume_GetChannelVolume
,
426 channelvolume_SetAllVolumes
,
427 channelvolume_GetAllVolumes
430 static HRESULT WINAPI
simplevolume_QueryInterface(ISimpleAudioVolume
*iface
, REFIID riid
,
433 TRACE("(%p)->(%s, %p)\n", iface
, debugstr_guid(riid
), ppv
);
438 if (IsEqualIID(riid
, &IID_IUnknown
) ||
439 IsEqualIID(riid
, &IID_ISimpleAudioVolume
))
443 return E_NOINTERFACE
;
446 IUnknown_AddRef((IUnknown
*)*ppv
);
451 static ULONG WINAPI
simplevolume_AddRef(ISimpleAudioVolume
*iface
)
453 struct audio_session_wrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
454 return IAudioSessionControl2_AddRef(&This
->IAudioSessionControl2_iface
);
457 static ULONG WINAPI
simplevolume_Release(ISimpleAudioVolume
*iface
)
459 struct audio_session_wrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
460 return IAudioSessionControl2_Release(&This
->IAudioSessionControl2_iface
);
463 static HRESULT WINAPI
simplevolume_SetMasterVolume(ISimpleAudioVolume
*iface
, float level
,
466 struct audio_session_wrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
467 struct audio_session
*session
= This
->session
;
468 struct audio_client
*client
;
470 TRACE("(%p)->(%f, %s)\n", session
, level
, wine_dbgstr_guid(context
));
472 if (level
< 0.f
|| level
> 1.f
)
476 FIXME("Notifications not supported yet\n");
480 session
->master_vol
= level
;
482 LIST_FOR_EACH_ENTRY(client
, &session
->clients
, struct audio_client
, entry
)
483 set_stream_volumes(client
);
490 static HRESULT WINAPI
simplevolume_GetMasterVolume(ISimpleAudioVolume
*iface
, float *level
)
492 struct audio_session_wrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
493 struct audio_session
*session
= This
->session
;
495 TRACE("(%p)->(%p)\n", session
, level
);
500 *level
= session
->master_vol
;
505 static HRESULT WINAPI
simplevolume_SetMute(ISimpleAudioVolume
*iface
, BOOL mute
,
508 struct audio_session_wrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
509 struct audio_session
*session
= This
->session
;
510 struct audio_client
*client
;
512 TRACE("(%p)->(%u, %s)\n", session
, mute
, debugstr_guid(context
));
515 FIXME("Notifications not supported yet\n");
519 session
->mute
= mute
;
521 LIST_FOR_EACH_ENTRY(client
, &session
->clients
, struct audio_client
, entry
)
522 set_stream_volumes(client
);
529 static HRESULT WINAPI
simplevolume_GetMute(ISimpleAudioVolume
*iface
, BOOL
*mute
)
531 struct audio_session_wrapper
*This
= impl_from_ISimpleAudioVolume(iface
);
532 struct audio_session
*session
= This
->session
;
534 TRACE("(%p)->(%p)\n", session
, mute
);
539 *mute
= session
->mute
;
544 static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl
=
546 simplevolume_QueryInterface
,
548 simplevolume_Release
,
549 simplevolume_SetMasterVolume
,
550 simplevolume_GetMasterVolume
,
551 simplevolume_SetMute
,
555 static void session_init_vols(struct audio_session
*session
, UINT channels
)
557 if (session
->channel_count
< channels
) {
560 if (session
->channel_vols
)
561 session
->channel_vols
= HeapReAlloc(GetProcessHeap(), 0, session
->channel_vols
,
562 sizeof(float) * channels
);
564 session
->channel_vols
= HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels
);
566 if (!session
->channel_vols
)
569 for (i
= session
->channel_count
; i
< channels
; i
++)
570 session
->channel_vols
[i
] = 1.f
;
572 session
->channel_count
= channels
;
576 static struct audio_session
*session_create(const GUID
*guid
, IMMDevice
*device
, UINT channels
)
578 struct audio_session
*ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
579 sizeof(struct audio_session
));
583 memcpy(&ret
->guid
, guid
, sizeof(GUID
));
585 ret
->device
= device
;
587 list_init(&ret
->clients
);
589 list_add_head(&sessions
, &ret
->entry
);
591 session_init_vols(ret
, channels
);
593 ret
->master_vol
= 1.f
;
598 struct audio_session_wrapper
*session_wrapper_create(struct audio_client
*client
)
600 struct audio_session_wrapper
*ret
;
602 ret
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(struct audio_session_wrapper
));
606 ret
->IAudioSessionControl2_iface
.lpVtbl
= &AudioSessionControl2_Vtbl
;
607 ret
->IChannelAudioVolume_iface
.lpVtbl
= &ChannelAudioVolume_Vtbl
;
608 ret
->ISimpleAudioVolume_iface
.lpVtbl
= &SimpleAudioVolume_Vtbl
;
611 ret
->client
= client
;
614 ret
->session
= client
->session
;
615 IAudioClient3_AddRef(&client
->IAudioClient3_iface
);
621 /* If channels == 0, then this will return or create a session with
622 * matching dataflow and GUID. Otherwise, channels must also match. */
623 HRESULT
get_audio_session(const GUID
*guid
, IMMDevice
*device
, UINT channels
,
624 struct audio_session
**out
)
626 struct audio_session
*session
;
628 TRACE("(%s, %p, %u, %p)\n", debugstr_guid(guid
), device
, channels
, out
);
630 if (!guid
|| IsEqualGUID(guid
, &GUID_NULL
)) {
631 *out
= session_create(&GUID_NULL
, device
, channels
);
633 return E_OUTOFMEMORY
;
639 LIST_FOR_EACH_ENTRY(session
, &sessions
, struct audio_session
, entry
) {
640 if (session
->device
== device
&& IsEqualGUID(guid
, &session
->guid
)) {
641 session_init_vols(session
, channels
);
648 *out
= session_create(guid
, device
, channels
);
650 return E_OUTOFMEMORY
;
656 HRESULT
get_audio_session_wrapper(const GUID
*guid
, IMMDevice
*device
,
657 struct audio_session_wrapper
**out
)
659 struct audio_session
*session
;
661 const HRESULT hr
= get_audio_session(guid
, device
, 0, &session
);
665 *out
= session_wrapper_create(NULL
);
667 return E_OUTOFMEMORY
;
669 (*out
)->session
= session
;