From 95679266d475fb91c73f88936a5131adab255b76 Mon Sep 17 00:00:00 2001 From: Maarten Lankhorst Date: Thu, 15 Mar 2012 10:40:51 +0100 Subject: [PATCH] winepulse: Add session support --- Copied verbatim from winealsa --- dlls/winepulse.drv/mmdevdrv.c | 849 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 848 insertions(+), 1 deletion(-) diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c index b7414c2d83f..64ee62ea94f 100644 --- a/dlls/winepulse.drv/mmdevdrv.c +++ b/dlls/winepulse.drv/mmdevdrv.c @@ -69,6 +69,7 @@ static pa_mainloop *pulse_ml; static HANDLE pulse_thread; static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER; +static struct list g_sessions = LIST_INIT(g_sessions); /* Mixer format + period times */ static WAVEFORMATEXTENSIBLE pulse_fmt[2]; @@ -105,6 +106,31 @@ BOOL WINAPI DllMain(HINSTANCE dll, DWORD reason, void *reserved) typedef struct ACImpl ACImpl; +typedef struct _AudioSession { + GUID guid; + struct list clients; + + IMMDevice *device; + + float master_vol; + UINT32 channel_count; + float *channel_vols; + BOOL mute; + + struct list entry; +} AudioSession; + +typedef struct _AudioSessionWrapper { + IAudioSessionControl2 IAudioSessionControl2_iface; + IChannelAudioVolume IChannelAudioVolume_iface; + ISimpleAudioVolume ISimpleAudioVolume_iface; + + LONG ref; + + ACImpl *client; + AudioSession *session; +} AudioSessionWrapper; + typedef struct _ACPacket { struct list entry; UINT64 qpcpos; @@ -139,6 +165,8 @@ struct ACImpl { INT64 clock_lastpos, clock_written; pa_usec_t clock_pulse; + AudioSession *session; + AudioSessionWrapper *session_wrapper; struct list packet_free_head; struct list packet_filled_head; }; @@ -148,10 +176,15 @@ static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0}; static const IAudioClientVtbl AudioClient_Vtbl; static const IAudioRenderClientVtbl AudioRenderClient_Vtbl; static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl; +static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl; +static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl; +static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl; static const IAudioClockVtbl AudioClock_Vtbl; static const IAudioClock2Vtbl AudioClock2_Vtbl; static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl; +static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client); + static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface) { return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface); @@ -167,6 +200,21 @@ static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface) return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface); } +static inline AudioSessionWrapper *impl_from_IAudioSessionControl2(IAudioSessionControl2 *iface) +{ + return CONTAINING_RECORD(iface, AudioSessionWrapper, IAudioSessionControl2_iface); +} + +static inline AudioSessionWrapper *impl_from_ISimpleAudioVolume(ISimpleAudioVolume *iface) +{ + return CONTAINING_RECORD(iface, AudioSessionWrapper, ISimpleAudioVolume_iface); +} + +static inline AudioSessionWrapper *impl_from_IChannelAudioVolume(IChannelAudioVolume *iface) +{ + return CONTAINING_RECORD(iface, AudioSessionWrapper, IChannelAudioVolume_iface); +} + static inline ACImpl *impl_from_IAudioClock(IAudioClock *iface) { return CONTAINING_RECORD(iface, ACImpl, IAudioClock_iface); @@ -897,6 +945,85 @@ static DWORD get_channel_mask(unsigned int channels) return 0; } +static void session_init_vols(AudioSession *session, UINT channels) +{ + if (session->channel_count < channels) { + UINT i; + + if (session->channel_vols) + session->channel_vols = HeapReAlloc(GetProcessHeap(), 0, + session->channel_vols, sizeof(float) * channels); + else + session->channel_vols = HeapAlloc(GetProcessHeap(), 0, + sizeof(float) * channels); + if (!session->channel_vols) + return; + + for(i = session->channel_count; i < channels; ++i) + session->channel_vols[i] = 1.f; + + session->channel_count = channels; + } +} + +static AudioSession *create_session(const GUID *guid, IMMDevice *device, + UINT num_channels) +{ + AudioSession *ret; + + ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AudioSession)); + if (!ret) + return NULL; + + memcpy(&ret->guid, guid, sizeof(GUID)); + + ret->device = device; + + list_init(&ret->clients); + + list_add_head(&g_sessions, &ret->entry); + + session_init_vols(ret, num_channels); + + ret->master_vol = 1.f; + + return ret; +} + +/* if channels == 0, then this will return or create a session with + * matching dataflow and GUID. otherwise, channels must also match */ +static HRESULT get_audio_session(const GUID *sessionguid, + IMMDevice *device, UINT channels, AudioSession **out) +{ + AudioSession *session; + + if (!sessionguid || IsEqualGUID(sessionguid, &GUID_NULL)) { + *out = create_session(&GUID_NULL, device, channels); + if (!*out) + return E_OUTOFMEMORY; + + return S_OK; + } + + *out = NULL; + LIST_FOR_EACH_ENTRY(session, &g_sessions, AudioSession, entry) { + if (session->device == device && + IsEqualGUID(sessionguid, &session->guid)) { + session_init_vols(session, channels); + *out = session; + break; + } + } + + if (!*out) { + *out = create_session(sessionguid, device, channels); + if (!*out) + return E_OUTOFMEMORY; + } + + return S_OK; +} + static HRESULT pulse_spec_from_waveformat(ACImpl *This, const WAVEFORMATEX *fmt) { pa_channel_map_init(&This->map); @@ -1083,6 +1210,10 @@ static HRESULT WINAPI AudioClient_Initialize(IAudioClient *iface, assert(!capture_packets || data - This->bufsize_bytes == This->tmp_buffer); } } + if (SUCCEEDED(hr)) + hr = get_audio_session(sessionguid, This->parent, fmt->nChannels, &This->session); + if (SUCCEEDED(hr)) + list_add_tail(&This->session->clients, &This->entry); exit: if (FAILED(hr)) { @@ -1474,6 +1605,20 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid, *ppv = &This->IAudioClock_iface; } else if (IsEqualIID(riid, &IID_IAudioStreamVolume)) { *ppv = &This->IAudioStreamVolume_iface; + } else if (IsEqualIID(riid, &IID_IAudioSessionControl) || + IsEqualIID(riid, &IID_IChannelAudioVolume) || + IsEqualIID(riid, &IID_ISimpleAudioVolume)) { + if (!This->session_wrapper) { + This->session_wrapper = AudioSessionWrapper_Create(This); + if (!This->session_wrapper) + return E_OUTOFMEMORY; + } + if (IsEqualIID(riid, &IID_IAudioSessionControl)) + *ppv = &This->session_wrapper->IAudioSessionControl2_iface; + else if (IsEqualIID(riid, &IID_IChannelAudioVolume)) + *ppv = &This->session_wrapper->IChannelAudioVolume_iface; + else if (IsEqualIID(riid, &IID_ISimpleAudioVolume)) + *ppv = &This->session_wrapper->ISimpleAudioVolume_iface; } if (*ppv) { @@ -2158,9 +2303,711 @@ static const IAudioStreamVolumeVtbl AudioStreamVolume_Vtbl = AudioStreamVolume_GetAllVolumes }; +static AudioSessionWrapper *AudioSessionWrapper_Create(ACImpl *client) +{ + AudioSessionWrapper *ret; + + ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(AudioSessionWrapper)); + if (!ret) + return NULL; + + ret->IAudioSessionControl2_iface.lpVtbl = &AudioSessionControl2_Vtbl; + ret->ISimpleAudioVolume_iface.lpVtbl = &SimpleAudioVolume_Vtbl; + ret->IChannelAudioVolume_iface.lpVtbl = &ChannelAudioVolume_Vtbl; + + ret->ref = !client; + + ret->client = client; + if (client) { + ret->session = client->session; + AudioClient_AddRef(&client->IAudioClient_iface); + } + + return ret; +} + +static HRESULT WINAPI AudioSessionControl_QueryInterface( + IAudioSessionControl2 *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if (!ppv) + return E_POINTER; + *ppv = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAudioSessionControl) || + IsEqualIID(riid, &IID_IAudioSessionControl2)) + *ppv = iface; + if (*ppv) { + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI AudioSessionControl_AddRef(IAudioSessionControl2 *iface) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + ULONG ref; + ref = InterlockedIncrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + return ref; +} + +static ULONG WINAPI AudioSessionControl_Release(IAudioSessionControl2 *iface) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + ULONG ref; + ref = InterlockedDecrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + if (!ref) { + if (This->client) { + This->client->session_wrapper = NULL; + AudioClient_Release(&This->client->IAudioClient_iface); + } + HeapFree(GetProcessHeap(), 0, This); + } + return ref; +} + +static HRESULT WINAPI AudioSessionControl_GetState(IAudioSessionControl2 *iface, + AudioSessionState *state) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + ACImpl *client; + + TRACE("(%p)->(%p)\n", This, state); + + if (!state) + return NULL_PTR_ERR; + + pthread_mutex_lock(&pulse_lock); + if (list_empty(&This->session->clients)) { + *state = AudioSessionStateExpired; + goto out; + } + LIST_FOR_EACH_ENTRY(client, &This->session->clients, ACImpl, entry) { + if (client->started) { + *state = AudioSessionStateActive; + goto out; + } + } + *state = AudioSessionStateInactive; + +out: + pthread_mutex_unlock(&pulse_lock); + return S_OK; +} + +static HRESULT WINAPI AudioSessionControl_GetDisplayName( + IAudioSessionControl2 *iface, WCHAR **name) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, name); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_SetDisplayName( + IAudioSessionControl2 *iface, const WCHAR *name, const GUID *session) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p, %s) - stub\n", This, name, debugstr_guid(session)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetIconPath( + IAudioSessionControl2 *iface, WCHAR **path) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, path); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_SetIconPath( + IAudioSessionControl2 *iface, const WCHAR *path, const GUID *session) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p, %s) - stub\n", This, path, debugstr_guid(session)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetGroupingParam( + IAudioSessionControl2 *iface, GUID *group) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, group); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_SetGroupingParam( + IAudioSessionControl2 *iface, const GUID *group, const GUID *session) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%s, %s) - stub\n", This, debugstr_guid(group), + debugstr_guid(session)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_RegisterAudioSessionNotification( + IAudioSessionControl2 *iface, IAudioSessionEvents *events) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, events); + + return S_OK; +} + +static HRESULT WINAPI AudioSessionControl_UnregisterAudioSessionNotification( + IAudioSessionControl2 *iface, IAudioSessionEvents *events) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, events); + + return S_OK; +} + +static HRESULT WINAPI AudioSessionControl_GetSessionIdentifier( + IAudioSessionControl2 *iface, WCHAR **id) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetSessionInstanceIdentifier( + IAudioSessionControl2 *iface, WCHAR **id) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + FIXME("(%p)->(%p) - stub\n", This, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionControl_GetProcessId( + IAudioSessionControl2 *iface, DWORD *pid) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + TRACE("(%p)->(%p)\n", This, pid); + + if (!pid) + return E_POINTER; + + *pid = GetCurrentProcessId(); + + return S_OK; +} + +static HRESULT WINAPI AudioSessionControl_IsSystemSoundsSession( + IAudioSessionControl2 *iface) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + TRACE("(%p)\n", This); + + return S_FALSE; +} + +static HRESULT WINAPI AudioSessionControl_SetDuckingPreference( + IAudioSessionControl2 *iface, BOOL optout) +{ + AudioSessionWrapper *This = impl_from_IAudioSessionControl2(iface); + + TRACE("(%p)->(%d)\n", This, optout); + + return S_OK; +} + +static const IAudioSessionControl2Vtbl AudioSessionControl2_Vtbl = +{ + AudioSessionControl_QueryInterface, + AudioSessionControl_AddRef, + AudioSessionControl_Release, + AudioSessionControl_GetState, + AudioSessionControl_GetDisplayName, + AudioSessionControl_SetDisplayName, + AudioSessionControl_GetIconPath, + AudioSessionControl_SetIconPath, + AudioSessionControl_GetGroupingParam, + AudioSessionControl_SetGroupingParam, + AudioSessionControl_RegisterAudioSessionNotification, + AudioSessionControl_UnregisterAudioSessionNotification, + AudioSessionControl_GetSessionIdentifier, + AudioSessionControl_GetSessionInstanceIdentifier, + AudioSessionControl_GetProcessId, + AudioSessionControl_IsSystemSoundsSession, + AudioSessionControl_SetDuckingPreference +}; + +typedef struct _SessionMgr { + IAudioSessionManager2 IAudioSessionManager2_iface; + + LONG ref; + + IMMDevice *device; +} SessionMgr; + +static HRESULT WINAPI AudioSessionManager_QueryInterface(IAudioSessionManager2 *iface, + REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if (!ppv) + return E_POINTER; + *ppv = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAudioSessionManager) || + IsEqualIID(riid, &IID_IAudioSessionManager2)) + *ppv = iface; + if (*ppv) { + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static inline SessionMgr *impl_from_IAudioSessionManager2(IAudioSessionManager2 *iface) +{ + return CONTAINING_RECORD(iface, SessionMgr, IAudioSessionManager2_iface); +} + +static ULONG WINAPI AudioSessionManager_AddRef(IAudioSessionManager2 *iface) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + ULONG ref; + ref = InterlockedIncrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + return ref; +} + +static ULONG WINAPI AudioSessionManager_Release(IAudioSessionManager2 *iface) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + ULONG ref; + ref = InterlockedDecrement(&This->ref); + TRACE("(%p) Refcount now %u\n", This, ref); + if (!ref) + HeapFree(GetProcessHeap(), 0, This); + return ref; +} + +static HRESULT WINAPI AudioSessionManager_GetAudioSessionControl( + IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, + IAudioSessionControl **out) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + AudioSession *session; + AudioSessionWrapper *wrapper; + HRESULT hr; + + TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), + flags, out); + + hr = get_audio_session(session_guid, This->device, 0, &session); + if (FAILED(hr)) + return hr; + + wrapper = AudioSessionWrapper_Create(NULL); + if (!wrapper) + return E_OUTOFMEMORY; + + wrapper->session = session; + + *out = (IAudioSessionControl*)&wrapper->IAudioSessionControl2_iface; + + return S_OK; +} + +static HRESULT WINAPI AudioSessionManager_GetSimpleAudioVolume( + IAudioSessionManager2 *iface, const GUID *session_guid, DWORD flags, + ISimpleAudioVolume **out) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + AudioSession *session; + AudioSessionWrapper *wrapper; + HRESULT hr; + + TRACE("(%p)->(%s, %x, %p)\n", This, debugstr_guid(session_guid), + flags, out); + + hr = get_audio_session(session_guid, This->device, 0, &session); + if (FAILED(hr)) + return hr; + + wrapper = AudioSessionWrapper_Create(NULL); + if (!wrapper) + return E_OUTOFMEMORY; + + wrapper->session = session; + + *out = &wrapper->ISimpleAudioVolume_iface; + + return S_OK; +} + +static HRESULT WINAPI AudioSessionManager_GetSessionEnumerator( + IAudioSessionManager2 *iface, IAudioSessionEnumerator **out) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, out); + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionManager_RegisterSessionNotification( + IAudioSessionManager2 *iface, IAudioSessionNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionManager_UnregisterSessionNotification( + IAudioSessionManager2 *iface, IAudioSessionNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionManager_RegisterDuckNotification( + IAudioSessionManager2 *iface, const WCHAR *session_id, + IAudioVolumeDuckNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +static HRESULT WINAPI AudioSessionManager_UnregisterDuckNotification( + IAudioSessionManager2 *iface, + IAudioVolumeDuckNotification *notification) +{ + SessionMgr *This = impl_from_IAudioSessionManager2(iface); + FIXME("(%p)->(%p) - stub\n", This, notification); + return E_NOTIMPL; +} + +static const IAudioSessionManager2Vtbl AudioSessionManager2_Vtbl = +{ + AudioSessionManager_QueryInterface, + AudioSessionManager_AddRef, + AudioSessionManager_Release, + AudioSessionManager_GetAudioSessionControl, + AudioSessionManager_GetSimpleAudioVolume, + AudioSessionManager_GetSessionEnumerator, + AudioSessionManager_RegisterSessionNotification, + AudioSessionManager_UnregisterSessionNotification, + AudioSessionManager_RegisterDuckNotification, + AudioSessionManager_UnregisterDuckNotification +}; + +static HRESULT WINAPI SimpleAudioVolume_QueryInterface( + ISimpleAudioVolume *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if (!ppv) + return E_POINTER; + *ppv = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_ISimpleAudioVolume)) + *ppv = iface; + if (*ppv) { + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI SimpleAudioVolume_AddRef(ISimpleAudioVolume *iface) +{ + AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); + return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface); +} + +static ULONG WINAPI SimpleAudioVolume_Release(ISimpleAudioVolume *iface) +{ + AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); + return AudioSessionControl_Release(&This->IAudioSessionControl2_iface); +} + +static HRESULT WINAPI SimpleAudioVolume_SetMasterVolume( + ISimpleAudioVolume *iface, float level, const GUID *context) +{ + AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); + AudioSession *session = This->session; + + TRACE("(%p)->(%f, %s)\n", session, level, wine_dbgstr_guid(context)); + + if (level < 0.f || level > 1.f) + return E_INVALIDARG; + + if (context) + FIXME("Notifications not supported yet\n"); + + TRACE("Pulseaudio does not support session volume control\n"); + + pthread_mutex_lock(&pulse_lock); + session->master_vol = level; + pthread_mutex_unlock(&pulse_lock); + + return S_OK; +} + +static HRESULT WINAPI SimpleAudioVolume_GetMasterVolume( + ISimpleAudioVolume *iface, float *level) +{ + AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); + AudioSession *session = This->session; + + TRACE("(%p)->(%p)\n", session, level); + + if (!level) + return NULL_PTR_ERR; + + *level = session->master_vol; + + return S_OK; +} + +static HRESULT WINAPI SimpleAudioVolume_SetMute(ISimpleAudioVolume *iface, + BOOL mute, const GUID *context) +{ + AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); + AudioSession *session = This->session; + + TRACE("(%p)->(%u, %p)\n", session, mute, context); + + if (context) + FIXME("Notifications not supported yet\n"); + + session->mute = mute; + + return S_OK; +} + +static HRESULT WINAPI SimpleAudioVolume_GetMute(ISimpleAudioVolume *iface, + BOOL *mute) +{ + AudioSessionWrapper *This = impl_from_ISimpleAudioVolume(iface); + AudioSession *session = This->session; + + TRACE("(%p)->(%p)\n", session, mute); + + if (!mute) + return NULL_PTR_ERR; + + *mute = session->mute; + + return S_OK; +} + +static const ISimpleAudioVolumeVtbl SimpleAudioVolume_Vtbl = +{ + SimpleAudioVolume_QueryInterface, + SimpleAudioVolume_AddRef, + SimpleAudioVolume_Release, + SimpleAudioVolume_SetMasterVolume, + SimpleAudioVolume_GetMasterVolume, + SimpleAudioVolume_SetMute, + SimpleAudioVolume_GetMute +}; + +static HRESULT WINAPI ChannelAudioVolume_QueryInterface( + IChannelAudioVolume *iface, REFIID riid, void **ppv) +{ + TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + + if (!ppv) + return E_POINTER; + *ppv = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IChannelAudioVolume)) + *ppv = iface; + if (*ppv) { + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + WARN("Unknown interface %s\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI ChannelAudioVolume_AddRef(IChannelAudioVolume *iface) +{ + AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); + return AudioSessionControl_AddRef(&This->IAudioSessionControl2_iface); +} + +static ULONG WINAPI ChannelAudioVolume_Release(IChannelAudioVolume *iface) +{ + AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); + return AudioSessionControl_Release(&This->IAudioSessionControl2_iface); +} + +static HRESULT WINAPI ChannelAudioVolume_GetChannelCount( + IChannelAudioVolume *iface, UINT32 *out) +{ + AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); + AudioSession *session = This->session; + + TRACE("(%p)->(%p)\n", session, out); + + if (!out) + return NULL_PTR_ERR; + + *out = session->channel_count; + + return S_OK; +} + +static HRESULT WINAPI ChannelAudioVolume_SetChannelVolume( + IChannelAudioVolume *iface, UINT32 index, float level, + const GUID *context) +{ + AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); + AudioSession *session = This->session; + + TRACE("(%p)->(%d, %f, %s)\n", session, index, level, + wine_dbgstr_guid(context)); + + if (level < 0.f || level > 1.f) + return E_INVALIDARG; + + if (index >= session->channel_count) + return E_INVALIDARG; + + if (context) + FIXME("Notifications not supported yet\n"); + + TRACE("Pulseaudio does not support session volume control\n"); + + pthread_mutex_lock(&pulse_lock); + session->channel_vols[index] = level; + pthread_mutex_unlock(&pulse_lock); + + return S_OK; +} + +static HRESULT WINAPI ChannelAudioVolume_GetChannelVolume( + IChannelAudioVolume *iface, UINT32 index, float *level) +{ + AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); + AudioSession *session = This->session; + + TRACE("(%p)->(%d, %p)\n", session, index, level); + + if (!level) + return NULL_PTR_ERR; + + if (index >= session->channel_count) + return E_INVALIDARG; + + *level = session->channel_vols[index]; + + return S_OK; +} + +static HRESULT WINAPI ChannelAudioVolume_SetAllVolumes( + IChannelAudioVolume *iface, UINT32 count, const float *levels, + const GUID *context) +{ + AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); + AudioSession *session = This->session; + int i; + + TRACE("(%p)->(%d, %p, %s)\n", session, count, levels, + wine_dbgstr_guid(context)); + + if (!levels) + return NULL_PTR_ERR; + + if (count != session->channel_count) + return E_INVALIDARG; + + if (context) + FIXME("Notifications not supported yet\n"); + + TRACE("Pulseaudio does not support session volume control\n"); + + pthread_mutex_lock(&pulse_lock); + for(i = 0; i < count; ++i) + session->channel_vols[i] = levels[i]; + pthread_mutex_unlock(&pulse_lock); + return S_OK; +} + +static HRESULT WINAPI ChannelAudioVolume_GetAllVolumes( + IChannelAudioVolume *iface, UINT32 count, float *levels) +{ + AudioSessionWrapper *This = impl_from_IChannelAudioVolume(iface); + AudioSession *session = This->session; + int i; + + TRACE("(%p)->(%d, %p)\n", session, count, levels); + + if (!levels) + return NULL_PTR_ERR; + + if (count != session->channel_count) + return E_INVALIDARG; + + for(i = 0; i < count; ++i) + levels[i] = session->channel_vols[i]; + + return S_OK; +} + +static const IChannelAudioVolumeVtbl ChannelAudioVolume_Vtbl = +{ + ChannelAudioVolume_QueryInterface, + ChannelAudioVolume_AddRef, + ChannelAudioVolume_Release, + ChannelAudioVolume_GetChannelCount, + ChannelAudioVolume_SetChannelVolume, + ChannelAudioVolume_GetChannelVolume, + ChannelAudioVolume_SetAllVolumes, + ChannelAudioVolume_GetAllVolumes +}; + HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device, IAudioSessionManager2 **out) { + SessionMgr *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SessionMgr)); *out = NULL; - return E_NOTIMPL; + if (!This) + return E_OUTOFMEMORY; + This->IAudioSessionManager2_iface.lpVtbl = &AudioSessionManager2_Vtbl; + This->device = device; + This->ref = 1; + *out = &This->IAudioSessionManager2_iface; + return S_OK; } -- 2.11.4.GIT