mirror of https://code.videolan.org/videolan/vlc
Merge branch 'mmdevice/vol-per-player' into 'master'
mmdevice: allow to set the volume per player rather than per session See merge request videolan/vlc!3599
This commit is contained in:
commit
b4adbe481e
|
@ -83,7 +83,11 @@ if HAVE_JACK
|
||||||
aout_LTLIBRARIES += libjack_plugin.la
|
aout_LTLIBRARIES += libjack_plugin.la
|
||||||
endif
|
endif
|
||||||
|
|
||||||
libmmdevice_plugin_la_SOURCES = audio_output/mmdevice.c audio_output/mmdevice.h
|
libmmdevice_plugin_la_SOURCES = audio_output/mmdevice.c \
|
||||||
|
audio_output/mmdevice.h \
|
||||||
|
audio_output/mmdevice_volume_control.h \
|
||||||
|
audio_output/mmdevice_session_volume.c \
|
||||||
|
audio_output/mmdevice_player_volume.c
|
||||||
libmmdevice_plugin_la_LIBADD = $(LIBCOM) $(LIBM)
|
libmmdevice_plugin_la_LIBADD = $(LIBCOM) $(LIBM)
|
||||||
libwinstore_plugin_la_SOURCES = audio_output/winstore.c audio_output/mmdevice.h
|
libwinstore_plugin_la_SOURCES = audio_output/winstore.c audio_output/mmdevice.h
|
||||||
libwinstore_plugin_la_LIBADD = $(LIBCOM) -lmmdevapi
|
libwinstore_plugin_la_LIBADD = $(LIBCOM) -lmmdevapi
|
||||||
|
|
|
@ -34,6 +34,8 @@
|
||||||
#include <mmdeviceapi.h>
|
#include <mmdeviceapi.h>
|
||||||
#include <endpointvolume.h>
|
#include <endpointvolume.h>
|
||||||
|
|
||||||
|
#include "mmdevice_volume_control.h"
|
||||||
|
|
||||||
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd,
|
DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd,
|
||||||
0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
|
0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
|
||||||
|
|
||||||
|
@ -89,16 +91,15 @@ typedef struct
|
||||||
IMMDevice *dev; /**< Selected output device, NULL if none */
|
IMMDevice *dev; /**< Selected output device, NULL if none */
|
||||||
|
|
||||||
struct IMMNotificationClient device_events;
|
struct IMMNotificationClient device_events;
|
||||||
struct IAudioSessionEvents session_events;
|
|
||||||
struct IAudioVolumeDuckNotification duck;
|
struct IAudioVolumeDuckNotification duck;
|
||||||
|
|
||||||
|
float gain;
|
||||||
|
mmdevice_volume_controler_t* volume;
|
||||||
|
|
||||||
LONG refs;
|
LONG refs;
|
||||||
unsigned ducks;
|
unsigned ducks;
|
||||||
float gain; /**< Current software gain volume */
|
|
||||||
|
|
||||||
wchar_t *requested_device; /**< Requested device identifier, NULL if none */
|
wchar_t *requested_device; /**< Requested device identifier, NULL if none */
|
||||||
float requested_volume; /**< Requested volume, negative if none */
|
|
||||||
signed char requested_mute; /**< Requested mute, negative if none */
|
|
||||||
wchar_t *acquired_device; /**< Acquired device identifier, NULL if none */
|
wchar_t *acquired_device; /**< Acquired device identifier, NULL if none */
|
||||||
bool request_device_restart;
|
bool request_device_restart;
|
||||||
HANDLE work_event;
|
HANDLE work_event;
|
||||||
|
@ -173,30 +174,20 @@ static void Flush(audio_output_t *aout)
|
||||||
static int VolumeSetLocked(audio_output_t *aout, float vol)
|
static int VolumeSetLocked(audio_output_t *aout, float vol)
|
||||||
{
|
{
|
||||||
aout_sys_t *sys = aout->sys;
|
aout_sys_t *sys = aout->sys;
|
||||||
float gain = 1.f;
|
return mmdevice_volume_controler_request_volume(sys->volume, vol, &sys->gain);
|
||||||
|
|
||||||
vol = vol * vol * vol; /* ISimpleAudioVolume is tapered linearly. */
|
|
||||||
|
|
||||||
if (vol > 1.f)
|
|
||||||
{
|
|
||||||
gain = vol;
|
|
||||||
vol = 1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
sys->gain = gain;
|
|
||||||
sys->requested_volume = vol;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int VolumeSet(audio_output_t *aout, float vol)
|
static int VolumeSet(audio_output_t *aout, float vol)
|
||||||
{
|
{
|
||||||
aout_sys_t *sys = aout->sys;
|
aout_sys_t *sys = aout->sys;
|
||||||
|
|
||||||
vlc_mutex_lock(&sys->lock);
|
vlc_mutex_lock(&sys->lock);
|
||||||
int ret = VolumeSetLocked(aout, vol);
|
int ret = VolumeSetLocked(aout, vol);
|
||||||
aout_GainRequest(aout, sys->gain);
|
|
||||||
vlc_mutex_unlock(&sys->lock);
|
vlc_mutex_unlock(&sys->lock);
|
||||||
SetEvent(sys->work_event);
|
|
||||||
|
aout_GainRequest(sys->aout, sys->gain);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,176 +196,11 @@ static int MuteSet(audio_output_t *aout, bool mute)
|
||||||
aout_sys_t *sys = aout->sys;
|
aout_sys_t *sys = aout->sys;
|
||||||
|
|
||||||
vlc_mutex_lock(&sys->lock);
|
vlc_mutex_lock(&sys->lock);
|
||||||
sys->requested_mute = mute;
|
int ret = mmdevice_volume_controler_request_mute(sys->volume, mute);
|
||||||
vlc_mutex_unlock(&sys->lock);
|
vlc_mutex_unlock(&sys->lock);
|
||||||
SetEvent(sys->work_event);
|
return ret;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Audio session events ***/
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_QueryInterface(IAudioSessionEvents *this, REFIID riid,
|
|
||||||
void **ppv)
|
|
||||||
{
|
|
||||||
if (IsEqualIID(riid, &IID_IUnknown)
|
|
||||||
|| IsEqualIID(riid, &IID_IAudioSessionEvents))
|
|
||||||
{
|
|
||||||
*ppv = this;
|
|
||||||
IUnknown_AddRef(this);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*ppv = NULL;
|
|
||||||
return E_NOINTERFACE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP_(ULONG)
|
|
||||||
vlc_AudioSessionEvents_AddRef(IAudioSessionEvents *this)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
return InterlockedIncrement(&sys->refs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP_(ULONG)
|
|
||||||
vlc_AudioSessionEvents_Release(IAudioSessionEvents *this)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
return InterlockedDecrement(&sys->refs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_OnDisplayNameChanged(IAudioSessionEvents *this,
|
|
||||||
LPCWSTR wname, LPCGUID ctx)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
audio_output_t *aout = sys->aout;
|
|
||||||
|
|
||||||
msg_Dbg(aout, "display name changed: %ls", wname);
|
|
||||||
(void) ctx;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_OnIconPathChanged(IAudioSessionEvents *this,
|
|
||||||
LPCWSTR wpath, LPCGUID ctx)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
audio_output_t *aout = sys->aout;
|
|
||||||
|
|
||||||
msg_Dbg(aout, "icon path changed: %ls", wpath);
|
|
||||||
(void) ctx;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_OnSimpleVolumeChanged(IAudioSessionEvents *this,
|
|
||||||
float vol, BOOL mute,
|
|
||||||
LPCGUID ctx)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
audio_output_t *aout = sys->aout;
|
|
||||||
|
|
||||||
msg_Dbg(aout, "simple volume changed: %f, muting %sabled", vol,
|
|
||||||
mute ? "en" : "dis");
|
|
||||||
SetEvent(sys->work_event); /* implicit state: vol & mute */
|
|
||||||
(void) ctx;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_OnChannelVolumeChanged(IAudioSessionEvents *this,
|
|
||||||
DWORD count, float *vols,
|
|
||||||
DWORD changed, LPCGUID ctx)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
audio_output_t *aout = sys->aout;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_OnGroupingParamChanged(IAudioSessionEvents *this,
|
|
||||||
LPCGUID param, LPCGUID ctx)
|
|
||||||
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
audio_output_t *aout = sys->aout;
|
|
||||||
|
|
||||||
msg_Dbg(aout, "grouping parameter changed");
|
|
||||||
(void) param;
|
|
||||||
(void) ctx;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_OnStateChanged(IAudioSessionEvents *this,
|
|
||||||
AudioSessionState state)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
audio_output_t *aout = sys->aout;
|
|
||||||
|
|
||||||
msg_Dbg(aout, "state changed: %d", state);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
|
||||||
vlc_AudioSessionEvents_OnSessionDisconnected(IAudioSessionEvents *this,
|
|
||||||
AudioSessionDisconnectReason reason)
|
|
||||||
{
|
|
||||||
aout_sys_t *sys = container_of(this, aout_sys_t, session_events);
|
|
||||||
audio_output_t *aout = sys->aout;
|
|
||||||
|
|
||||||
switch (reason)
|
|
||||||
{
|
|
||||||
case DisconnectReasonDeviceRemoval:
|
|
||||||
msg_Warn(aout, "session disconnected: %s", "device removed");
|
|
||||||
break;
|
|
||||||
case DisconnectReasonServerShutdown:
|
|
||||||
msg_Err(aout, "session disconnected: %s", "service stopped");
|
|
||||||
return S_OK;
|
|
||||||
case DisconnectReasonFormatChanged:
|
|
||||||
msg_Warn(aout, "session disconnected: %s", "format changed");
|
|
||||||
break;
|
|
||||||
case DisconnectReasonSessionLogoff:
|
|
||||||
msg_Err(aout, "session disconnected: %s", "user logged off");
|
|
||||||
return S_OK;
|
|
||||||
case DisconnectReasonSessionDisconnected:
|
|
||||||
msg_Err(aout, "session disconnected: %s", "session disconnected");
|
|
||||||
return S_OK;
|
|
||||||
case DisconnectReasonExclusiveModeOverride:
|
|
||||||
msg_Err(aout, "session disconnected: %s", "stream overridden");
|
|
||||||
return S_OK;
|
|
||||||
default:
|
|
||||||
msg_Warn(aout, "session disconnected: unknown reason %d", reason);
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
/* NOTE: audio decoder thread should get invalidated device and restart */
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct IAudioSessionEventsVtbl vlc_AudioSessionEvents =
|
|
||||||
{
|
|
||||||
vlc_AudioSessionEvents_QueryInterface,
|
|
||||||
vlc_AudioSessionEvents_AddRef,
|
|
||||||
vlc_AudioSessionEvents_Release,
|
|
||||||
|
|
||||||
vlc_AudioSessionEvents_OnDisplayNameChanged,
|
|
||||||
vlc_AudioSessionEvents_OnIconPathChanged,
|
|
||||||
vlc_AudioSessionEvents_OnSimpleVolumeChanged,
|
|
||||||
vlc_AudioSessionEvents_OnChannelVolumeChanged,
|
|
||||||
vlc_AudioSessionEvents_OnGroupingParamChanged,
|
|
||||||
vlc_AudioSessionEvents_OnStateChanged,
|
|
||||||
vlc_AudioSessionEvents_OnSessionDisconnected,
|
|
||||||
};
|
|
||||||
|
|
||||||
static STDMETHODIMP
|
static STDMETHODIMP
|
||||||
vlc_AudioVolumeDuckNotification_QueryInterface(
|
vlc_AudioVolumeDuckNotification_QueryInterface(
|
||||||
|
@ -782,61 +608,15 @@ static int DeviceSelect(audio_output_t *aout, const char *id)
|
||||||
*
|
*
|
||||||
* Adjust volume as long as device is unchanged
|
* Adjust volume as long as device is unchanged
|
||||||
* */
|
* */
|
||||||
static void MMSessionMainloop(audio_output_t *aout, ISimpleAudioVolume *volume)
|
static void MMSessionMainloop(audio_output_t *aout)
|
||||||
{
|
{
|
||||||
aout_sys_t *sys = aout->sys;
|
aout_sys_t *sys = aout->sys;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
mmdevice_volume_controler_t* volume = sys->volume;
|
||||||
bool report_volume = true;
|
|
||||||
bool report_mute = true;
|
|
||||||
|
|
||||||
while (sys->requested_device == NULL)
|
while (sys->requested_device == NULL)
|
||||||
{
|
{
|
||||||
if (volume != NULL)
|
mmdevice_volume_controler_process(volume, sys->stream);
|
||||||
{
|
|
||||||
if (sys->requested_volume >= 0.f)
|
|
||||||
{
|
|
||||||
hr = ISimpleAudioVolume_SetMasterVolume(volume, sys->requested_volume, NULL);
|
|
||||||
if (FAILED(hr))
|
|
||||||
msg_Err(aout, "cannot set master volume (error 0x%lX)",
|
|
||||||
hr);
|
|
||||||
report_volume = true;
|
|
||||||
sys->requested_volume = -1.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (report_volume)
|
|
||||||
{
|
|
||||||
float level;
|
|
||||||
hr = ISimpleAudioVolume_GetMasterVolume(volume, &level);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
aout_VolumeReport(aout, cbrtf(level * sys->gain));
|
|
||||||
else
|
|
||||||
msg_Err(aout, "cannot get master volume (error 0x%lX)", hr);
|
|
||||||
report_volume = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sys->requested_mute >= 0)
|
|
||||||
{
|
|
||||||
BOOL mute = sys->requested_mute ? TRUE : FALSE;
|
|
||||||
|
|
||||||
hr = ISimpleAudioVolume_SetMute(volume, mute, NULL);
|
|
||||||
if (FAILED(hr))
|
|
||||||
msg_Err(aout, "cannot set mute (error 0x%lX)", hr);
|
|
||||||
report_mute = true;
|
|
||||||
sys->requested_mute = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (report_mute)
|
|
||||||
{
|
|
||||||
BOOL mute;
|
|
||||||
hr = ISimpleAudioVolume_GetMute(volume, &mute);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
aout_MuteReport(aout, mute != FALSE);
|
|
||||||
else
|
|
||||||
msg_Err(aout, "cannot get mute (error 0x%lX)", hr);
|
|
||||||
report_mute = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD wait_ms = INFINITE;
|
DWORD wait_ms = INFINITE;
|
||||||
DWORD ev_count = 1;
|
DWORD ev_count = 1;
|
||||||
|
@ -890,7 +670,6 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
|
||||||
aout_sys_t *sys = aout->sys;
|
aout_sys_t *sys = aout->sys;
|
||||||
IAudioSessionManager *manager;
|
IAudioSessionManager *manager;
|
||||||
IAudioSessionControl *control;
|
IAudioSessionControl *control;
|
||||||
ISimpleAudioVolume *volume;
|
|
||||||
IAudioEndpointVolume *endpoint;
|
IAudioEndpointVolume *endpoint;
|
||||||
void *pv;
|
void *pv;
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
|
@ -961,14 +740,14 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LPCGUID guid = var_GetBool(aout, "volume-save") ? &GUID_VLC_AUD_OUT : NULL;
|
||||||
|
|
||||||
/* Create session manager (for controls even w/o active audio client) */
|
/* Create session manager (for controls even w/o active audio client) */
|
||||||
hr = IMMDevice_Activate(sys->dev, &IID_IAudioSessionManager,
|
hr = IMMDevice_Activate(sys->dev, &IID_IAudioSessionManager,
|
||||||
CLSCTX_ALL, NULL, &pv);
|
CLSCTX_ALL, NULL, &pv);
|
||||||
manager = pv;
|
manager = pv;
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
LPCGUID guid = var_GetBool(aout, "volume-save") ? &GUID_VLC_AUD_OUT : NULL;
|
|
||||||
|
|
||||||
/* Register session control */
|
/* Register session control */
|
||||||
hr = IAudioSessionManager_GetAudioSessionControl(manager, guid, 0,
|
hr = IAudioSessionManager_GetAudioSessionControl(manager, guid, 0,
|
||||||
&control);
|
&control);
|
||||||
|
@ -985,17 +764,12 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
|
||||||
}
|
}
|
||||||
free(ua);
|
free(ua);
|
||||||
}
|
}
|
||||||
|
|
||||||
IAudioSessionControl_RegisterAudioSessionNotification(control,
|
|
||||||
&sys->session_events);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
control = NULL;
|
||||||
msg_Err(aout, "cannot get session control (error 0x%lX)", hr);
|
msg_Err(aout, "cannot get session control (error 0x%lX)", hr);
|
||||||
|
}
|
||||||
hr = IAudioSessionManager_GetSimpleAudioVolume(manager, guid, FALSE,
|
|
||||||
&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 */
|
/* Try to get version 2 (Windows 7) of the manager & control */
|
||||||
wchar_t *siid = NULL;
|
wchar_t *siid = NULL;
|
||||||
|
@ -1031,11 +805,9 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
|
||||||
CoTaskMemFree(siid);
|
CoTaskMemFree(siid);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
msg_Err(aout, "cannot activate session manager (error 0x%lX)", hr);
|
msg_Err(aout, "cannot activate session manager (error 0x%lX)", hr);
|
||||||
control = NULL;
|
|
||||||
volume = NULL;
|
mmdevice_volume_controler_initialize(sys->volume, manager, guid);
|
||||||
}
|
|
||||||
|
|
||||||
hr = IMMDevice_Activate(sys->dev, &IID_IAudioEndpointVolume,
|
hr = IMMDevice_Activate(sys->dev, &IID_IAudioEndpointVolume,
|
||||||
CLSCTX_ALL, NULL, &pv);
|
CLSCTX_ALL, NULL, &pv);
|
||||||
|
@ -1054,10 +826,14 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
|
||||||
else
|
else
|
||||||
msg_Err(aout, "cannot activate endpoint volume (error 0x%lX)", hr);
|
msg_Err(aout, "cannot activate endpoint volume (error 0x%lX)", hr);
|
||||||
|
|
||||||
MMSessionMainloop(aout, volume);
|
MMSessionMainloop(aout);
|
||||||
|
|
||||||
vlc_mutex_unlock(&sys->lock);
|
vlc_mutex_unlock(&sys->lock);
|
||||||
|
|
||||||
|
if (sys->volume)
|
||||||
|
mmdevice_volume_controler_release(sys->volume);
|
||||||
|
sys->volume = NULL;
|
||||||
|
|
||||||
if (endpoint != NULL)
|
if (endpoint != NULL)
|
||||||
IAudioEndpointVolume_Release(endpoint);
|
IAudioEndpointVolume_Release(endpoint);
|
||||||
|
|
||||||
|
@ -1073,15 +849,8 @@ static HRESULT MMSession(audio_output_t *aout, IMMDeviceEnumerator *it)
|
||||||
IAudioSessionManager2_Release(m2);
|
IAudioSessionManager2_Release(m2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (volume != NULL)
|
|
||||||
ISimpleAudioVolume_Release(volume);
|
|
||||||
|
|
||||||
if (control != NULL)
|
if (control != NULL)
|
||||||
{
|
|
||||||
IAudioSessionControl_UnregisterAudioSessionNotification(control,
|
|
||||||
&sys->session_events);
|
|
||||||
IAudioSessionControl_Release(control);
|
IAudioSessionControl_Release(control);
|
||||||
}
|
|
||||||
|
|
||||||
IAudioSessionManager_Release(manager);
|
IAudioSessionManager_Release(manager);
|
||||||
}
|
}
|
||||||
|
@ -1330,20 +1099,12 @@ static int Open(vlc_object_t *obj)
|
||||||
sys->it = NULL;
|
sys->it = NULL;
|
||||||
sys->dev = NULL;
|
sys->dev = NULL;
|
||||||
sys->device_events.lpVtbl = &vlc_MMNotificationClient;
|
sys->device_events.lpVtbl = &vlc_MMNotificationClient;
|
||||||
sys->session_events.lpVtbl = &vlc_AudioSessionEvents;
|
|
||||||
sys->duck.lpVtbl = &vlc_AudioVolumeDuckNotification;
|
sys->duck.lpVtbl = &vlc_AudioVolumeDuckNotification;
|
||||||
sys->refs = 1;
|
sys->refs = 1;
|
||||||
sys->ducks = 0;
|
sys->ducks = 0;
|
||||||
|
|
||||||
sys->gain = 1.f;
|
sys->gain = 1.f;
|
||||||
sys->requested_volume = -1.f;
|
|
||||||
sys->requested_mute = -1;
|
|
||||||
sys->acquired_device = NULL;
|
sys->acquired_device = NULL;
|
||||||
sys->request_device_restart = false;
|
sys->request_device_restart = false;
|
||||||
|
|
||||||
if (!var_CreateGetBool(aout, "volume-save"))
|
|
||||||
VolumeSetLocked(aout, var_InheritFloat(aout, "mmdevice-volume"));
|
|
||||||
|
|
||||||
vlc_mutex_init(&sys->lock);
|
vlc_mutex_init(&sys->lock);
|
||||||
vlc_cond_init(&sys->ready);
|
vlc_cond_init(&sys->ready);
|
||||||
|
|
||||||
|
@ -1351,6 +1112,17 @@ static int Open(vlc_object_t *obj)
|
||||||
if (unlikely(sys->work_event == NULL))
|
if (unlikely(sys->work_event == NULL))
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
if (var_InheritBool(aout, "mmdevice-session-volume"))
|
||||||
|
sys->volume = createMMDeviceSessionVolumeControler(aout, sys->work_event);
|
||||||
|
else
|
||||||
|
sys->volume = createMMDevicePlayerVolumeControler(aout, sys->work_event);
|
||||||
|
|
||||||
|
if (unlikely(sys->volume == NULL))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (!var_CreateGetBool(aout, "volume-save"))
|
||||||
|
VolumeSetLocked(aout, var_InheritFloat(aout, "mmdevice-volume"));
|
||||||
|
|
||||||
aout_HotplugReport(aout, default_device_b, _("Default"));
|
aout_HotplugReport(aout, default_device_b, _("Default"));
|
||||||
|
|
||||||
char *saved_device_b = var_InheritString(aout, "mmdevice-audio-device");
|
char *saved_device_b = var_InheritString(aout, "mmdevice-audio-device");
|
||||||
|
@ -1535,6 +1307,10 @@ static const char *const ppsz_mmdevice_passthrough_texts[] = {
|
||||||
#define VOLUME_TEXT N_("Audio volume")
|
#define VOLUME_TEXT N_("Audio volume")
|
||||||
#define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).")
|
#define VOLUME_LONGTEXT N_("Audio volume in hundredths of decibels (dB).")
|
||||||
|
|
||||||
|
|
||||||
|
#define SESSION_VOLUME_TEXT N_("Use session volume")
|
||||||
|
#define SESSION_VOLUME_LONGTEXT N_("Use session volume, or per player volume when disabled")
|
||||||
|
|
||||||
vlc_module_begin()
|
vlc_module_begin()
|
||||||
set_shortname("MMDevice")
|
set_shortname("MMDevice")
|
||||||
set_description(N_("Windows Multimedia Device output"))
|
set_description(N_("Windows Multimedia Device output"))
|
||||||
|
@ -1550,4 +1326,5 @@ vlc_module_begin()
|
||||||
add_string("mmdevice-audio-device", NULL, DEVICE_TEXT, DEVICE_LONGTEXT)
|
add_string("mmdevice-audio-device", NULL, DEVICE_TEXT, DEVICE_LONGTEXT)
|
||||||
add_float("mmdevice-volume", 1.f, VOLUME_TEXT, VOLUME_LONGTEXT)
|
add_float("mmdevice-volume", 1.f, VOLUME_TEXT, VOLUME_LONGTEXT)
|
||||||
change_float_range( 0.f, 1.25f )
|
change_float_range( 0.f, 1.25f )
|
||||||
|
add_bool("mmdevice-session-volume", true, SESSION_VOLUME_TEXT, SESSION_VOLUME_LONGTEXT)
|
||||||
vlc_module_end()
|
vlc_module_end()
|
||||||
|
|
|
@ -40,6 +40,7 @@ struct aout_stream
|
||||||
HRESULT (*play)(aout_stream_t *, block_t *, vlc_tick_t);
|
HRESULT (*play)(aout_stream_t *, block_t *, vlc_tick_t);
|
||||||
HRESULT (*pause)(aout_stream_t *, bool);
|
HRESULT (*pause)(aout_stream_t *, bool);
|
||||||
HRESULT (*flush)(aout_stream_t *);
|
HRESULT (*flush)(aout_stream_t *);
|
||||||
|
HRESULT (*getservice)(aout_stream_t *, REFIID riid, void **ppv);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct aout_stream_owner
|
struct aout_stream_owner
|
||||||
|
@ -110,6 +111,12 @@ HRESULT aout_stream_owner_Flush(struct aout_stream_owner *owner)
|
||||||
return owner->s.flush(&owner->s);
|
return owner->s.flush(&owner->s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline HRESULT aout_stream_GetService(struct aout_stream_owner *owner,
|
||||||
|
REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
return owner->s.getservice(&owner->s, riid, ppv);
|
||||||
|
}
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
void aout_stream_owner_AppendBlock(struct aout_stream_owner *owner,
|
void aout_stream_owner_AppendBlock(struct aout_stream_owner *owner,
|
||||||
block_t *block, vlc_tick_t date)
|
block_t *block, vlc_tick_t date)
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright (C) 2012-2023 VLC authors and VideoLAN
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "mmdevice_volume_control.h"
|
||||||
|
#include <vlc_aout.h>
|
||||||
|
#include "mmdevice.h"
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
mmdevice_volume_controler_t iface;
|
||||||
|
|
||||||
|
audio_output_t *aout;
|
||||||
|
|
||||||
|
float requested_volume; /**< volume */
|
||||||
|
signed char requested_mute; /**< mute */
|
||||||
|
|
||||||
|
bool report_mute;
|
||||||
|
bool report_volume;
|
||||||
|
|
||||||
|
float volume;
|
||||||
|
float gain; /**< gain */
|
||||||
|
|
||||||
|
HANDLE work_event;
|
||||||
|
|
||||||
|
} mmdevice_player_volume_controler_t;
|
||||||
|
|
||||||
|
#define VOLUME_CONTROL_IMPL_FROM_IFACE(obj) \
|
||||||
|
container_of((obj), mmdevice_player_volume_controler_t, iface)
|
||||||
|
|
||||||
|
static int RequestMute(mmdevice_volume_controler_t* controler, bool mute)
|
||||||
|
{
|
||||||
|
mmdevice_player_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
sys->requested_mute = mute;
|
||||||
|
SetEvent(sys->work_event);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int RequestVolume(mmdevice_volume_controler_t* controler, float value, float* outGain)
|
||||||
|
{
|
||||||
|
mmdevice_player_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
|
||||||
|
float gain = 1.f;
|
||||||
|
|
||||||
|
value = value * value * value; /* ISimpleAudioVolume is tapered linearly. */
|
||||||
|
|
||||||
|
if (value > 1.f)
|
||||||
|
{
|
||||||
|
gain = value;
|
||||||
|
value = 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
sys->requested_volume = value;
|
||||||
|
sys->gain =gain;
|
||||||
|
if (outGain != NULL)
|
||||||
|
*outGain = gain;
|
||||||
|
SetEvent(sys->work_event);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetAllStreamVolume(IAudioStreamVolume *stream_volume, UINT32 chan_count, float volume)
|
||||||
|
{
|
||||||
|
float chan_volumes[chan_count];
|
||||||
|
for (UINT32 i = 0; i < chan_count; ++i)
|
||||||
|
chan_volumes[i] = volume;
|
||||||
|
IAudioStreamVolume_SetAllVolumes(stream_volume, chan_count, chan_volumes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Process(mmdevice_volume_controler_t* controler, struct aout_stream_owner *stream)
|
||||||
|
{
|
||||||
|
mmdevice_player_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
|
||||||
|
if (!stream
|
||||||
|
|| (sys->requested_mute < 0 && sys->requested_volume < 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
IAudioStreamVolume *stream_volume = NULL;
|
||||||
|
|
||||||
|
void* pv;
|
||||||
|
HRESULT hr = aout_stream_GetService(stream, &IID_IAudioStreamVolume, &pv);
|
||||||
|
if (FAILED(hr))
|
||||||
|
goto error;
|
||||||
|
stream_volume = pv;
|
||||||
|
|
||||||
|
UINT32 chan_count;
|
||||||
|
hr = IAudioStreamVolume_GetChannelCount(stream_volume, &chan_count);
|
||||||
|
if (FAILED(hr))
|
||||||
|
goto error;
|
||||||
|
assert(chan_count <= 64);
|
||||||
|
|
||||||
|
if (chan_count == 0)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (sys->requested_mute >= 0)
|
||||||
|
{
|
||||||
|
if (sys->requested_mute)
|
||||||
|
SetAllStreamVolume(stream_volume, chan_count, 0.f);
|
||||||
|
else
|
||||||
|
SetAllStreamVolume(stream_volume, chan_count, sys->volume);
|
||||||
|
|
||||||
|
sys->report_mute = true;
|
||||||
|
sys->requested_mute = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys->requested_volume >= 0.f)
|
||||||
|
{
|
||||||
|
SetAllStreamVolume(stream_volume, chan_count, sys->requested_volume);
|
||||||
|
|
||||||
|
sys->volume = sys->requested_volume;
|
||||||
|
sys->report_volume = true;
|
||||||
|
sys->requested_volume = -1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float currentVolume;
|
||||||
|
hr = IAudioStreamVolume_GetChannelVolume(stream_volume, 0,
|
||||||
|
¤tVolume);
|
||||||
|
if (FAILED(hr))
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
if (sys->report_mute)
|
||||||
|
{
|
||||||
|
aout_MuteReport(sys->aout, currentVolume == 0.f);
|
||||||
|
sys->report_mute =false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys->report_volume)
|
||||||
|
{
|
||||||
|
aout_VolumeReport(sys->aout, cbrtf(currentVolume * sys->gain));
|
||||||
|
sys->report_volume =false;
|
||||||
|
}
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (stream_volume)
|
||||||
|
IAudioStreamVolume_Release(stream_volume);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Initialize(struct mmdevice_volume_controler_t* controler, IAudioSessionManager *manager, LPCGUID guid)
|
||||||
|
{
|
||||||
|
VLC_UNUSED(controler);
|
||||||
|
VLC_UNUSED(manager);
|
||||||
|
VLC_UNUSED(guid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void Release(mmdevice_volume_controler_t* controler)
|
||||||
|
{
|
||||||
|
mmdevice_player_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
free(sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
mmdevice_volume_controler_t* createMMDevicePlayerVolumeControler(audio_output_t *aout, HANDLE work_event)
|
||||||
|
{
|
||||||
|
mmdevice_player_volume_controler_t* sys = malloc(sizeof(mmdevice_player_volume_controler_t));
|
||||||
|
if (!sys)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sys->aout = aout;
|
||||||
|
|
||||||
|
sys->requested_mute = -1;
|
||||||
|
sys->requested_volume = -1.f;
|
||||||
|
//report initial volume and mute state
|
||||||
|
sys->report_mute = true;
|
||||||
|
sys->report_volume = true;
|
||||||
|
|
||||||
|
sys->volume = 1.f;
|
||||||
|
sys->gain = 1.f;
|
||||||
|
sys->work_event = work_event;
|
||||||
|
|
||||||
|
sys->iface.release = Release;
|
||||||
|
|
||||||
|
sys->iface.initialize = Initialize;
|
||||||
|
sys->iface.process = Process;
|
||||||
|
|
||||||
|
sys->iface.requestMute = RequestMute;
|
||||||
|
sys->iface.requestVolume = RequestVolume;
|
||||||
|
|
||||||
|
return &sys->iface;
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,399 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* mmdevice_session_volume.c : Windows Multimedia Device API audio output plugin for VLC
|
||||||
|
*****************************************************************************
|
||||||
|
* Copyright (C) 2012-2017 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
|
||||||
|
* the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#include "mmdevice_volume_control.h"
|
||||||
|
|
||||||
|
#include <vlc_aout.h>
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
mmdevice_volume_controler_t iface;
|
||||||
|
|
||||||
|
audio_output_t *aout;
|
||||||
|
struct IAudioSessionEvents session_events;
|
||||||
|
ISimpleAudioVolume *volume;
|
||||||
|
IAudioSessionControl *control;
|
||||||
|
LONG refs;
|
||||||
|
HANDLE work_event;
|
||||||
|
vlc_mutex_t lock;
|
||||||
|
|
||||||
|
float gain; /**< Current software gain volume */
|
||||||
|
bool report_volume;
|
||||||
|
bool report_mute;
|
||||||
|
float requested_volume; /**< Requested volume, negative if none */
|
||||||
|
signed char requested_mute; /**< Requested mute, negative if none */
|
||||||
|
|
||||||
|
|
||||||
|
} mmdevice_session_volume_controler_t;
|
||||||
|
|
||||||
|
#define VOLUME_CONTROL_IMPL_FROM_IFACE(obj) \
|
||||||
|
container_of((obj), mmdevice_session_volume_controler_t, iface)
|
||||||
|
|
||||||
|
/*** Audio session events ***/
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_QueryInterface(IAudioSessionEvents *this, REFIID riid,
|
||||||
|
void **ppv)
|
||||||
|
{
|
||||||
|
if (IsEqualIID(riid, &IID_IUnknown)
|
||||||
|
|| IsEqualIID(riid, &IID_IAudioSessionEvents))
|
||||||
|
{
|
||||||
|
*ppv = this;
|
||||||
|
IUnknown_AddRef(this);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*ppv = NULL;
|
||||||
|
return E_NOINTERFACE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP_(ULONG)
|
||||||
|
vlc_AudioSessionEvents_AddRef(IAudioSessionEvents *this)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
return InterlockedIncrement(&sys->refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP_(ULONG)
|
||||||
|
vlc_AudioSessionEvents_Release(IAudioSessionEvents *this)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
return InterlockedDecrement(&sys->refs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_OnDisplayNameChanged(IAudioSessionEvents *this,
|
||||||
|
LPCWSTR wname, LPCGUID ctx)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
msg_Dbg(aout, "display name changed: %ls", wname);
|
||||||
|
(void) ctx;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_OnIconPathChanged(IAudioSessionEvents *this,
|
||||||
|
LPCWSTR wpath, LPCGUID ctx)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
msg_Dbg(aout, "icon path changed: %ls", wpath);
|
||||||
|
(void) ctx;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_OnSimpleVolumeChanged(IAudioSessionEvents *this,
|
||||||
|
float vol, BOOL mute,
|
||||||
|
LPCGUID ctx)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
msg_Dbg(aout, "simple volume changed: %f, muting %sabled", vol,
|
||||||
|
mute ? "en" : "dis");
|
||||||
|
|
||||||
|
vlc_mutex_lock(&sys->lock);
|
||||||
|
sys->report_volume = true;
|
||||||
|
sys->report_mute = true;
|
||||||
|
vlc_mutex_unlock(&sys->lock);
|
||||||
|
SetEvent(sys->work_event);
|
||||||
|
|
||||||
|
(void) ctx;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_OnChannelVolumeChanged(IAudioSessionEvents *this,
|
||||||
|
DWORD count, float *vols,
|
||||||
|
DWORD changed, LPCGUID ctx)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_OnGroupingParamChanged(IAudioSessionEvents *this,
|
||||||
|
LPCGUID param, LPCGUID ctx)
|
||||||
|
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
msg_Dbg(aout, "grouping parameter changed");
|
||||||
|
(void) param;
|
||||||
|
(void) ctx;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_OnStateChanged(IAudioSessionEvents *this,
|
||||||
|
AudioSessionState state)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
msg_Dbg(aout, "state changed: %d", state);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static STDMETHODIMP
|
||||||
|
vlc_AudioSessionEvents_OnSessionDisconnected(IAudioSessionEvents *this,
|
||||||
|
AudioSessionDisconnectReason reason)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = container_of(this, mmdevice_session_volume_controler_t, session_events);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
switch (reason)
|
||||||
|
{
|
||||||
|
case DisconnectReasonDeviceRemoval:
|
||||||
|
msg_Warn(aout, "session disconnected: %s", "device removed");
|
||||||
|
break;
|
||||||
|
case DisconnectReasonServerShutdown:
|
||||||
|
msg_Err(aout, "session disconnected: %s", "service stopped");
|
||||||
|
return S_OK;
|
||||||
|
case DisconnectReasonFormatChanged:
|
||||||
|
msg_Warn(aout, "session disconnected: %s", "format changed");
|
||||||
|
break;
|
||||||
|
case DisconnectReasonSessionLogoff:
|
||||||
|
msg_Err(aout, "session disconnected: %s", "user logged off");
|
||||||
|
return S_OK;
|
||||||
|
case DisconnectReasonSessionDisconnected:
|
||||||
|
msg_Err(aout, "session disconnected: %s", "session disconnected");
|
||||||
|
return S_OK;
|
||||||
|
case DisconnectReasonExclusiveModeOverride:
|
||||||
|
msg_Err(aout, "session disconnected: %s", "stream overridden");
|
||||||
|
return S_OK;
|
||||||
|
default:
|
||||||
|
msg_Warn(aout, "session disconnected: unknown reason %d", reason);
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
/* NOTE: audio decoder thread should get invalidated device and restart */
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct IAudioSessionEventsVtbl vlc_AudioSessionEvents =
|
||||||
|
{
|
||||||
|
vlc_AudioSessionEvents_QueryInterface,
|
||||||
|
vlc_AudioSessionEvents_AddRef,
|
||||||
|
vlc_AudioSessionEvents_Release,
|
||||||
|
|
||||||
|
vlc_AudioSessionEvents_OnDisplayNameChanged,
|
||||||
|
vlc_AudioSessionEvents_OnIconPathChanged,
|
||||||
|
vlc_AudioSessionEvents_OnSimpleVolumeChanged,
|
||||||
|
vlc_AudioSessionEvents_OnChannelVolumeChanged,
|
||||||
|
vlc_AudioSessionEvents_OnGroupingParamChanged,
|
||||||
|
vlc_AudioSessionEvents_OnStateChanged,
|
||||||
|
vlc_AudioSessionEvents_OnSessionDisconnected,
|
||||||
|
};
|
||||||
|
|
||||||
|
static void Process(mmdevice_volume_controler_t *controler, struct aout_stream_owner * stream)
|
||||||
|
{
|
||||||
|
VLC_UNUSED(stream);
|
||||||
|
HRESULT hr;
|
||||||
|
mmdevice_session_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
audio_output_t *aout = sys->aout;
|
||||||
|
|
||||||
|
vlc_mutex_lock(&sys->lock);
|
||||||
|
|
||||||
|
if (sys->requested_volume >= 0.f)
|
||||||
|
{
|
||||||
|
hr = ISimpleAudioVolume_SetMasterVolume(sys->volume, sys->requested_volume, NULL);
|
||||||
|
if (FAILED(hr))
|
||||||
|
msg_Err(aout, "cannot set master volume (error 0x%lX)", hr);
|
||||||
|
sys->requested_volume = -1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (sys->report_volume)
|
||||||
|
{
|
||||||
|
float level;
|
||||||
|
hr = ISimpleAudioVolume_GetMasterVolume(sys->volume, &level);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
aout_VolumeReport(aout, cbrtf(level * sys->gain));
|
||||||
|
else
|
||||||
|
msg_Err(aout, "cannot get master volume (error 0x%lX)", hr);
|
||||||
|
|
||||||
|
sys->report_volume = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys->requested_mute >= 0)
|
||||||
|
{
|
||||||
|
BOOL mute = sys->requested_mute ? TRUE : FALSE;
|
||||||
|
|
||||||
|
hr = ISimpleAudioVolume_SetMute(sys->volume, mute, NULL);
|
||||||
|
if (FAILED(hr))
|
||||||
|
msg_Err(aout, "cannot set mute (error 0x%lX)", hr);
|
||||||
|
sys->requested_mute = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys->report_mute)
|
||||||
|
{
|
||||||
|
BOOL mute;
|
||||||
|
hr = ISimpleAudioVolume_GetMute(sys->volume, &mute);
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
aout_MuteReport(aout, mute != FALSE);
|
||||||
|
else
|
||||||
|
msg_Err(aout, "cannot get mute (error 0x%lX)", hr);
|
||||||
|
|
||||||
|
sys->report_mute = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vlc_mutex_unlock(&sys->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int RequestMute(mmdevice_volume_controler_t *controler, bool mute)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
|
||||||
|
vlc_mutex_lock(&sys->lock);
|
||||||
|
{
|
||||||
|
sys->requested_mute = mute;
|
||||||
|
sys->report_mute = true;
|
||||||
|
}
|
||||||
|
vlc_mutex_unlock(&sys->lock);
|
||||||
|
|
||||||
|
SetEvent(sys->work_event);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int RequestVolume(mmdevice_volume_controler_t *controler, float vol, float* outGain)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
|
||||||
|
float gain = 1.f;
|
||||||
|
|
||||||
|
vol = vol * vol * vol; /* ISimpleAudioVolume is tapered linearly. */
|
||||||
|
|
||||||
|
if (vol > 1.f)
|
||||||
|
{
|
||||||
|
gain = vol;
|
||||||
|
vol = 1.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
vlc_mutex_lock(&sys->lock);
|
||||||
|
{
|
||||||
|
sys->requested_volume = vol;
|
||||||
|
sys->report_volume = true;
|
||||||
|
sys->gain =gain;
|
||||||
|
}
|
||||||
|
vlc_mutex_unlock(&sys->lock);
|
||||||
|
|
||||||
|
if (outGain != NULL)
|
||||||
|
*outGain = gain;
|
||||||
|
SetEvent(sys->work_event);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Release(mmdevice_volume_controler_t *controler)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
if (!sys)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (sys->volume != NULL)
|
||||||
|
ISimpleAudioVolume_Release(sys->volume);
|
||||||
|
|
||||||
|
if (sys->control)
|
||||||
|
{
|
||||||
|
IAudioSessionControl_UnregisterAudioSessionNotification(sys->control,
|
||||||
|
&sys->session_events);
|
||||||
|
IAudioSessionControl_Release(sys->control);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(sys);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Initialize(struct mmdevice_volume_controler_t* controler, IAudioSessionManager *manager, LPCGUID guid)
|
||||||
|
{
|
||||||
|
mmdevice_session_volume_controler_t *sys = VOLUME_CONTROL_IMPL_FROM_IFACE(controler);
|
||||||
|
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
IAudioSessionControl *control;
|
||||||
|
hr = IAudioSessionManager_GetAudioSessionControl(manager, guid, 0,
|
||||||
|
&control);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
sys->control = control;
|
||||||
|
|
||||||
|
IAudioSessionControl_RegisterAudioSessionNotification(control,
|
||||||
|
&sys->session_events);
|
||||||
|
|
||||||
|
|
||||||
|
hr = IAudioSessionManager_GetSimpleAudioVolume(manager, guid, FALSE,
|
||||||
|
&sys->volume);
|
||||||
|
if (FAILED(hr))
|
||||||
|
{
|
||||||
|
msg_Err(sys->aout, "cannot get simple volume (error 0x%lx)", hr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmdevice_volume_controler_t* createMMDeviceSessionVolumeControler(audio_output_t *aout, HANDLE workevent)
|
||||||
|
{
|
||||||
|
|
||||||
|
mmdevice_session_volume_controler_t* sys = malloc(sizeof(mmdevice_session_volume_controler_t));
|
||||||
|
if (!sys)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sys->aout = aout;
|
||||||
|
sys->refs = 1;
|
||||||
|
sys->volume = NULL;
|
||||||
|
sys->control = NULL;
|
||||||
|
sys->gain = 1.f;
|
||||||
|
sys->work_event = workevent;
|
||||||
|
vlc_mutex_init(&sys->lock);
|
||||||
|
|
||||||
|
sys->report_volume = true;
|
||||||
|
sys->report_mute = true;
|
||||||
|
sys->requested_volume = -1.f;
|
||||||
|
sys->requested_mute = -1;
|
||||||
|
|
||||||
|
sys->session_events.lpVtbl = &vlc_AudioSessionEvents;
|
||||||
|
|
||||||
|
sys->iface.release = Release;
|
||||||
|
sys->iface.requestMute = RequestMute;
|
||||||
|
sys->iface.requestVolume = RequestVolume;
|
||||||
|
|
||||||
|
sys->iface.initialize = Initialize;
|
||||||
|
sys->iface.process = Process;
|
||||||
|
|
||||||
|
return &sys->iface;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
/*****************************************************************************
|
||||||
|
* Copyright (C) 2012-2023 VLC authors and VideoLAN
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef MMDEVICE_VOLUME_H
|
||||||
|
#define MMDEVICE_VOLUME_H
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
# include <config.h>
|
||||||
|
#endif
|
||||||
|
#include <vlc_common.h>
|
||||||
|
|
||||||
|
#define INITGUID
|
||||||
|
#define COBJMACROS
|
||||||
|
#define CONST_VTABLE
|
||||||
|
|
||||||
|
#include <endpointvolume.h>
|
||||||
|
#include <audiopolicy.h>
|
||||||
|
|
||||||
|
struct aout_stream_owner;
|
||||||
|
|
||||||
|
struct mmdevice_volume_controler_t
|
||||||
|
{
|
||||||
|
void (*release)(struct mmdevice_volume_controler_t* volume);
|
||||||
|
|
||||||
|
int (*requestMute)(struct mmdevice_volume_controler_t* volume, bool mute);
|
||||||
|
int (*requestVolume)(struct mmdevice_volume_controler_t* volume, float vol, float* gain);
|
||||||
|
|
||||||
|
//initialize is called from the MM Thread
|
||||||
|
bool (*initialize)(struct mmdevice_volume_controler_t* volume, IAudioSessionManager *manager, LPCGUID guid);
|
||||||
|
//process is called from the MM Thread
|
||||||
|
void (*process)(struct mmdevice_volume_controler_t* volume, struct aout_stream_owner * stream);
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct mmdevice_volume_controler_t mmdevice_volume_controler_t;
|
||||||
|
|
||||||
|
inline static void mmdevice_volume_controler_release(mmdevice_volume_controler_t* controler)
|
||||||
|
{
|
||||||
|
controler->release(controler);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int mmdevice_volume_controler_request_mute(mmdevice_volume_controler_t* controler, bool mute)
|
||||||
|
{
|
||||||
|
return controler->requestMute(controler, mute);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static int mmdevice_volume_controler_request_volume(mmdevice_volume_controler_t* controler, float vol, float* gain)
|
||||||
|
{
|
||||||
|
return controler->requestVolume(controler, vol, gain);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static bool mmdevice_volume_controler_initialize(mmdevice_volume_controler_t* controler, IAudioSessionManager *manager, LPCGUID guid)
|
||||||
|
{
|
||||||
|
return controler->initialize(controler, manager, guid);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline static void mmdevice_volume_controler_process(mmdevice_volume_controler_t* controler, struct aout_stream_owner * stream)
|
||||||
|
{
|
||||||
|
controler->process(controler, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
mmdevice_volume_controler_t* createMMDeviceSessionVolumeControler(audio_output_t *aout, HANDLE work_event);
|
||||||
|
|
||||||
|
mmdevice_volume_controler_t* createMMDevicePlayerVolumeControler(audio_output_t *aout, HANDLE work_event);
|
||||||
|
|
||||||
|
#endif /* MMDEVICE_VOLUME_H */
|
|
@ -365,6 +365,12 @@ static HRESULT Flush(aout_stream_t *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static HRESULT GetService(aout_stream_t *s, REFIID riid, void **ppv)
|
||||||
|
{
|
||||||
|
aout_stream_sys_t *sys = s->sys;
|
||||||
|
return IAudioClient_GetService(sys->client, riid, ppv);
|
||||||
|
}
|
||||||
|
|
||||||
/*** Initialization / deinitialization **/
|
/*** Initialization / deinitialization **/
|
||||||
static const uint32_t chans_out[] = {
|
static const uint32_t chans_out[] = {
|
||||||
SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
|
SPEAKER_FRONT_LEFT, SPEAKER_FRONT_RIGHT,
|
||||||
|
@ -930,6 +936,7 @@ static HRESULT Start(aout_stream_t *s, audio_sample_format_t *restrict pfmt,
|
||||||
s->play = Play;
|
s->play = Play;
|
||||||
s->pause = Pause;
|
s->pause = Pause;
|
||||||
s->flush = Flush;
|
s->flush = Flush;
|
||||||
|
s->getservice = GetService;
|
||||||
s->stop = Stop;
|
s->stop = Stop;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
error:
|
error:
|
||||||
|
|
Loading…
Reference in New Issue