/*****************************************************************************
* mmdevice.c : Windows Multimedia Device API audio output plugin for VLC
*****************************************************************************
- * Copyright (C) 2012-2013 Rémi Denis-Courmont
+ * Copyright (C) 2012-2014 Rémi Denis-Courmont
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
IMMDevice *dev; /**< Selected output device, NULL if none */
struct IMMNotificationClient device_events;
- struct IAudioEndpointVolumeCallback endpoint_callback;
struct IAudioSessionEvents session_events;
+ struct IAudioVolumeDuckNotification duck;
LONG refs;
+ unsigned ducks;
+ float gain; /**< Current software gain volume */
wchar_t *device; /**< Requested device identifier, NULL if none */
float volume; /**< Requested volume, negative if none */
EnterMTA();
aout_stream_Flush(sys->stream, wait);
LeaveMTA();
-
}
static int VolumeSet(audio_output_t *aout, float vol)
{
aout_sys_t *sys = aout->sys;
+ float gain = 1.f;
vol = vol * vol * vol; /* ISimpleAudioVolume is tapered linearly. */
+
+ if (vol > 1.f)
+ {
+ gain = vol;
+ vol = 1.f;
+ }
+
+ aout_GainRequest(aout, gain);
+
EnterCriticalSection(&sys->lock);
+ sys->gain = gain;
sys->volume = vol;
WakeConditionVariable(&sys->work);
LeaveCriticalSection(&sys->lock);
static STDMETHODIMP
vlc_AudioSessionEvents_OnSimpleVolumeChanged(IAudioSessionEvents *this,
- float vol, WINBOOL mute,
+ float vol, BOOL mute,
LPCGUID ctx)
{
aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
aout_sys_t *sys = vlc_AudioSessionEvents_sys(this);
audio_output_t *aout = sys->aout;
- msg_Dbg(aout, "channel volume %lu of %lu changed: %f", changed, count,
- vols[changed]);
+ if (changed != (DWORD)-1)
+ msg_Dbg(aout, "channel volume %lu of %lu changed: %f", changed, count,
+ vols[changed]);
+ else
+ msg_Dbg(aout, "%lu channels volume changed", count);
+
(void) ctx;
return S_OK;
}
vlc_AudioSessionEvents_OnSessionDisconnected,
};
-
-/*** Audio endpoint volume ***/
-static inline aout_sys_t *vlc_AudioEndpointVolumeCallback_sys(IAudioEndpointVolumeCallback *this)
+static inline aout_sys_t *vlc_AudioVolumeDuckNotification_sys(IAudioVolumeDuckNotification *this)
{
- return (void *)(((char *)this) - offsetof(aout_sys_t, endpoint_callback));
+ return (void *)(((char *)this) - offsetof(aout_sys_t, duck));
}
static STDMETHODIMP
-vlc_AudioEndpointVolumeCallback_QueryInterface(IAudioEndpointVolumeCallback *this,
- REFIID riid, void **ppv)
+vlc_AudioVolumeDuckNotification_QueryInterface(
+ IAudioVolumeDuckNotification *this, REFIID riid, void **ppv)
{
if (IsEqualIID(riid, &IID_IUnknown)
- || IsEqualIID(riid, &IID_IAudioEndpointVolumeCallback))
+ || IsEqualIID(riid, &IID_IAudioVolumeDuckNotification))
{
*ppv = this;
IUnknown_AddRef(this);
}
static STDMETHODIMP_(ULONG)
-vlc_AudioEndpointVolumeCallback_AddRef(IAudioEndpointVolumeCallback *this)
+vlc_AudioVolumeDuckNotification_AddRef(IAudioVolumeDuckNotification *this)
{
- aout_sys_t *sys = vlc_AudioEndpointVolumeCallback_sys(this);
+ aout_sys_t *sys = vlc_AudioVolumeDuckNotification_sys(this);
return InterlockedIncrement(&sys->refs);
}
static STDMETHODIMP_(ULONG)
-vlc_AudioEndpointVolumeCallback_Release(IAudioEndpointVolumeCallback *this)
+vlc_AudioVolumeDuckNotification_Release(IAudioVolumeDuckNotification *this)
{
- aout_sys_t *sys = vlc_AudioEndpointVolumeCallback_sys(this);
+ aout_sys_t *sys = vlc_AudioVolumeDuckNotification_sys(this);
return InterlockedDecrement(&sys->refs);
}
static STDMETHODIMP
-vlc_AudioEndpointVolumeCallback_OnNotify(IAudioEndpointVolumeCallback *this,
- const PAUDIO_VOLUME_NOTIFICATION_DATA notify)
+vlc_AudioVolumeDuckNotification_OnVolumeDuckNotification(
+ IAudioVolumeDuckNotification *this, LPCWSTR sid, UINT32 count)
{
- aout_sys_t *sys = vlc_AudioEndpointVolumeCallback_sys(this);
+ aout_sys_t *sys = vlc_AudioVolumeDuckNotification_sys(this);
audio_output_t *aout = sys->aout;
- msg_Dbg(aout, "endpoint volume changed");
- EnterCriticalSection(&sys->lock);
- WakeConditionVariable(&sys->work); /* implicit state: endpoint volume */
- LeaveCriticalSection(&sys->lock);
- (void) notify;
+ msg_Dbg(aout, "volume ducked by %ls of %u sessions", sid, count);
+ sys->ducks++;
+ aout_PolicyReport(aout, true);
+ return S_OK;
+}
+
+static STDMETHODIMP
+vlc_AudioVolumeDuckNotification_OnVolumeUnduckNotification(
+ IAudioVolumeDuckNotification *this, LPCWSTR sid)
+{
+ aout_sys_t *sys = vlc_AudioVolumeDuckNotification_sys(this);
+ audio_output_t *aout = sys->aout;
+
+ msg_Dbg(aout, "volume unducked by %ls", sid);
+ sys->ducks--;
+ aout_PolicyReport(aout, sys->ducks != 0);
return S_OK;
}
-static const struct IAudioEndpointVolumeCallbackVtbl vlc_AudioEndpointVolumeCallback =
+static const struct IAudioVolumeDuckNotificationVtbl vlc_AudioVolumeDuckNotification =
{
- vlc_AudioEndpointVolumeCallback_QueryInterface,
- vlc_AudioEndpointVolumeCallback_AddRef,
- vlc_AudioEndpointVolumeCallback_Release,
+ vlc_AudioVolumeDuckNotification_QueryInterface,
+ vlc_AudioVolumeDuckNotification_AddRef,
+ vlc_AudioVolumeDuckNotification_Release,
- vlc_AudioEndpointVolumeCallback_OnNotify,
+ vlc_AudioVolumeDuckNotification_OnVolumeDuckNotification,
+ vlc_AudioVolumeDuckNotification_OnVolumeUnduckNotification,
};
IAudioEndpointVolume *endpoint;
void *pv;
HRESULT hr;
- float base_volume = 1.f;
assert(sys->device != NULL);
assert(sys->dev == NULL);
&volume);
if (FAILED(hr))
msg_Err(aout, "cannot get simple volume (error 0x%lx)", hr);
+
+ /* Try to get version 2 (Windows 7) of the manager & control */
+ wchar_t *siid = NULL;
+
+ hr = IAudioSessionManager_QueryInterface(manager,
+ &IID_IAudioSessionControl2, &pv);
+ if (SUCCEEDED(hr))
+ {
+ IAudioSessionControl2 *c2 = pv;
+
+ IAudioSessionControl2_SetDuckingPreference(c2, FALSE);
+ hr = IAudioSessionControl2_GetSessionInstanceIdentifier(c2, &siid);
+ if (FAILED(hr))
+ siid = NULL;
+ IAudioSessionControl2_Release(c2);
+ }
+ else
+ msg_Dbg(aout, "version 2 session control unavailable");
+
+ hr = IAudioSessionManager_QueryInterface(manager,
+ &IID_IAudioSessionManager2, &pv);
+ if (SUCCEEDED(hr))
+ {
+ IAudioSessionManager2 *m2 = pv;
+
+ IAudioSessionManager2_RegisterDuckNotification(m2, siid,
+ &sys->duck);
+ IAudioSessionManager2_Release(m2);
+ }
+ else
+ msg_Dbg(aout, "version 2 session management unavailable");
+
+ CoTaskMemFree(siid);
}
else
{
hr = IAudioEndpointVolume_GetVolumeRange(endpoint, &min, &max, &inc);
if (SUCCEEDED(hr))
- {
msg_Dbg(aout, "volume from %+f dB to %+f dB with %f dB increments",
min, max, inc);
- base_volume = powf(10.f, max / 20.f + .3f);
- }
else
msg_Err(aout, "cannot get volume range (error 0x%lx)", hr);
-
- IAudioEndpointVolume_RegisterControlChangeNotify(endpoint,
- &sys->endpoint_callback);
}
else
msg_Err(aout, "cannot activate endpoint volume (error %lx)", hr);
/* Main loop (adjust volume as long as device is unchanged) */
while (sys->device == NULL)
{
- float level = 1.f, master = 1.f;
-
if (volume != NULL)
{
- hr = ISimpleAudioVolume_GetMasterVolume(volume, &level);
- if (FAILED(hr))
- msg_Err(aout, "cannot get master volume (error 0x%lx)", hr);
- }
-
- if (endpoint != NULL)
- {
- float db;
+ float level;
- hr = IAudioEndpointVolume_GetMasterVolumeLevel(endpoint, &db);
+ hr = ISimpleAudioVolume_GetMasterVolume(volume, &level);
if (SUCCEEDED(hr))
- master = powf(10.f, db / 20.f);
+ aout_VolumeReport(aout, cbrtf(level * sys->gain));
else
- msg_Err(aout, "cannot get endpoint volume (error 0x%lx)", hr);
- }
-
- aout_VolumeReport(aout, cbrtf(level * master * base_volume));
-
- /* The WASAPI simple volume is relative to the endpoint volume, and it
- * cannot exceed 100%. Therefore the endpoint master volume must be
- * increased to reach an overall volume above the current endpoint master
- * volume. Unfortunately, that means the volume of other applications will
- * also be changed (which may or may not be what the user wants) and
- * introduces race conditions between updates. */
-
- level = sys->volume / base_volume;
- sys->volume = -1.f;
-
- if (level > master)
- {
- master = level;
- level = 1.f;
- }
- else
- {
- if (master > 0.f)
- level /= master;
- master = -1.f;
- }
+ msg_Err(aout, "cannot get master volume (error 0x%lx)", hr);
- if (volume != NULL)
- {
+ level = sys->volume;
if (level >= 0.f)
{
- assert(level <= 1.f);
hr = ISimpleAudioVolume_SetMasterVolume(volume, level, NULL);
if (FAILED(hr))
msg_Err(aout, "cannot set master volume (error 0x%lx)",
hr);
}
+ sys->volume = -1.f;
BOOL mute;
sys->mute = -1;
}
- if (endpoint != NULL && master >= 0.f)
- {
- float v = 20.f * log10f(master);
-
- msg_Warn(aout, "overriding endpoint volume: %+f dB", v);
- hr = IAudioEndpointVolume_SetMasterVolumeLevel(endpoint, v, NULL);
- if (FAILED(hr))
- msg_Err(aout, "cannot set endpoint volume (error 0x%lx)", hr);
- }
-
- sys->volume = -1.f;
-
SleepConditionVariableCS(&sys->work, &sys->lock, INFINITE);
}
LeaveCriticalSection(&sys->lock);
if (endpoint != NULL)
- {
- IAudioEndpointVolume_UnregisterControlChangeNotify(endpoint,
- &sys->endpoint_callback);
IAudioEndpointVolume_Release(endpoint);
- }
if (manager != NULL)
{ /* Deregister callbacks *without* the lock */
+ hr = IAudioSessionManager_QueryInterface(manager,
+ &IID_IAudioSessionManager2, &pv);
+ if (SUCCEEDED(hr))
+ {
+ IAudioSessionManager2 *m2 = pv;
+
+ IAudioSessionManager2_UnregisterDuckNotification(m2, &sys->duck);
+ IAudioSessionManager2_Release(m2);
+ }
+
if (volume != NULL)
ISimpleAudioVolume_Release(volume);
{
HRESULT hr;
- sys->module = vlc_module_load(s, "aout stream", NULL, false,
+ /* TODO: Do not overload the "aout" configuration item. */
+ sys->module = vlc_module_load(s, "aout stream", "$aout", false,
aout_stream_Start, s, fmt, &hr);
if (hr != AUDCLNT_E_DEVICE_INVALIDATED || DeviceSelect(aout, NULL))
break;
assert (sys->stream == NULL);
sys->stream = s;
+ aout_GainRequest(aout, sys->gain);
return 0;
}
{
aout_sys_t *sys = aout->sys;
- assert (sys->stream != NULL);
+ assert(sys->stream != NULL);
EnterMTA();
vlc_module_unload(sys->module, aout_stream_Stop, sys->stream);
sys->it = NULL;
sys->dev = NULL;
sys->device_events.lpVtbl = &vlc_MMNotificationClient;
- sys->endpoint_callback.lpVtbl = &vlc_AudioEndpointVolumeCallback;
sys->session_events.lpVtbl = &vlc_AudioSessionEvents;
+ sys->duck.lpVtbl = &vlc_AudioVolumeDuckNotification;
sys->refs = 1;
+ sys->ducks = 0;
sys->device = default_device;
+ sys->gain = 1.f;
sys->volume = -1.f;
sys->mute = -1;
InitializeCriticalSection(&sys->lock);
set_capability("audio output", 150)
set_category(CAT_AUDIO)
set_subcategory(SUBCAT_AUDIO_AOUT)
- add_shortcut("wasapi")
+ add_shortcut("wasapi", "directsound")
set_callbacks(Open, Close)
vlc_module_end()