summaryrefslogtreecommitdiffstats
path: root/audio/out
diff options
context:
space:
mode:
authorDiogo Franco (Kovensky) <diogomfranco@gmail.com>2014-03-11 02:50:49 -0300
committerDiogo Franco (Kovensky) <diogomfranco@gmail.com>2014-03-11 16:37:21 -0300
commitf8bdada77f992410deb4fc12652cf406d95e3657 (patch)
tree7d47a7100c192cdbba55dbe05962e47d47b52ca6 /audio/out
parentf3e9b9462210021286bfbe98eef228ab74c8b490 (diff)
downloadmpv-f8bdada77f992410deb4fc12652cf406d95e3657.tar.bz2
mpv-f8bdada77f992410deb4fc12652cf406d95e3657.tar.xz
ao_wasapi: Implement per-application mixing
The volume controls in mpv now affect the session's volume (the application's volume in the mixer). Since we do not request a non-persistent session, the volume and mute status persist across mpv invocations and system reboots. In exclusive mode, WASAPI doesn't have access to a mixer so the endpoint (sound card)'s master volume is modified instead. Since by definition mpv is the only thing outputting audio in exclusive mode, this causes no conflict, and ao_wasapi restores the last user-set volume when it's uninitialized.
Diffstat (limited to 'audio/out')
-rw-r--r--audio/out/ao_wasapi.c49
-rwxr-xr-xaudio/out/ao_wasapi.h5
-rwxr-xr-xaudio/out/ao_wasapi_utils.c57
3 files changed, 95 insertions, 16 deletions
diff --git a/audio/out/ao_wasapi.c b/audio/out/ao_wasapi.c
index a496ff9aa6..ec04cedcd6 100644
--- a/audio/out/ao_wasapi.c
+++ b/audio/out/ao_wasapi.c
@@ -195,6 +195,7 @@ static int init(struct ao *ao)
wasapi_enumerate_devices(state->log);
}
+ ao->per_application_mixer = true;
if (state->opt_exclusive) {
state->share_mode = AUDCLNT_SHAREMODE_EXCLUSIVE;
} else {
@@ -235,22 +236,54 @@ static int init(struct ao *ao)
static int control(struct ao *ao, enum aocontrol cmd, void *arg)
{
struct wasapi_state *state = (struct wasapi_state *)ao->priv;
- if (!state->share_mode && !(state->vol_hw_support & ENDPOINT_HARDWARE_SUPPORT_VOLUME)) {
- return CONTROL_UNKNOWN; /* hw does not support volume controls in exclusive mode */
- }
-
ao_control_vol_t *vol = (ao_control_vol_t *)arg;
+ BOOL mute;
switch (cmd) {
case AOCONTROL_GET_VOLUME:
- IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolumeProxy,
- &state->audio_volume);
+ if (state->opt_exclusive)
+ IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolumeProxy,
+ &state->audio_volume);
+ else
+ ISimpleAudioVolume_GetMasterVolume(state->pAudioVolumeProxy,
+ &state->audio_volume);
+
+ /* check to see if user manually changed volume through mixer;
+ this information is used in exclusive mode for restoring the mixer volume on uninit */
+ if (state->audio_volume != state->previous_volume) {
+ MP_VERBOSE(state, "mixer difference: %.2g now, expected %.2g\n",
+ state->audio_volume, state->previous_volume);
+ state->initial_volume = state->audio_volume;
+ }
+
vol->left = vol->right = 100.0f * state->audio_volume;
return CONTROL_OK;
case AOCONTROL_SET_VOLUME:
state->audio_volume = vol->left / 100.f;
- IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolumeProxy,
- state->audio_volume, NULL);
+ if (state->opt_exclusive)
+ IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolumeProxy,
+ state->audio_volume, NULL);
+ else
+ ISimpleAudioVolume_SetMasterVolume(state->pAudioVolumeProxy,
+ state->audio_volume, NULL);
+
+ state->previous_volume = state->audio_volume;
+ return CONTROL_OK;
+ case AOCONTROL_GET_MUTE:
+ if (state->opt_exclusive)
+ IAudioEndpointVolume_GetMute(state->pEndpointVolumeProxy, &mute);
+ else
+ ISimpleAudioVolume_GetMute(state->pAudioVolumeProxy, &mute);
+ *(bool*)arg = mute;
+
+ return CONTROL_OK;
+ case AOCONTROL_SET_MUTE:
+ mute = *(bool*)arg;
+ if (state->opt_exclusive)
+ IAudioEndpointVolume_SetMute(state->pEndpointVolumeProxy, mute, NULL);
+ else
+ ISimpleAudioVolume_SetMute(state->pAudioVolumeProxy, mute, NULL);
+
return CONTROL_OK;
default:
return CONTROL_UNKNOWN;
diff --git a/audio/out/ao_wasapi.h b/audio/out/ao_wasapi.h
index a0ce654d4d..d17b2504a0 100755
--- a/audio/out/ao_wasapi.h
+++ b/audio/out/ao_wasapi.h
@@ -41,6 +41,8 @@ typedef struct wasapi_state {
/* volume control */
DWORD vol_hw_support, status;
float audio_volume;
+ float previous_volume;
+ float initial_volume;
/* Buffers */
size_t buffer_block_size; /* Size of each block in bytes */
@@ -54,6 +56,7 @@ typedef struct wasapi_state {
IMMDevice *pDevice;
IAudioClient *pAudioClient;
IAudioRenderClient *pRenderClient;
+ ISimpleAudioVolume *pAudioVolume;
IAudioEndpointVolume *pEndpointVolume;
HANDLE hFeed; /* wasapi event */
HANDLE hForceFeed; /* forces writing a buffer (e.g. before audio_resume) */
@@ -65,12 +68,14 @@ typedef struct wasapi_state {
/* WASAPI proxy handles, for Single-Threaded Apartment communication.
One is needed for each object that's accessed by a different thread. */
IAudioClient *pAudioClientProxy;
+ ISimpleAudioVolume *pAudioVolumeProxy;
IAudioEndpointVolume *pEndpointVolumeProxy;
/* Streams used to marshal the proxy objects. The thread owning the actual objects
needs to marshal proxy objects into these streams, and the thread that wants the
proxies unmarshals them from here. */
IStream *sAudioClient;
+ IStream *sAudioVolume;
IStream *sEndpointVolume;
/* WASAPI internal clock information, for estimating delay */
diff --git a/audio/out/ao_wasapi_utils.c b/audio/out/ao_wasapi_utils.c
index feadfc0631..f5336cbebf 100755
--- a/audio/out/ao_wasapi_utils.c
+++ b/audio/out/ao_wasapi_utils.c
@@ -506,6 +506,11 @@ reinit:
&IID_IAudioRenderClient,
(void **)&state->pRenderClient);
EXIT_ON_ERROR(hr);
+ hr = IAudioClient_GetService(state->pAudioClient,
+ &IID_ISimpleAudioVolume,
+ (void **) &state->pAudioVolume);
+ EXIT_ON_ERROR(hr);
+
if (!state->hFeed)
goto exit_label;
hr = IAudioClient_SetEventHandle(state->pAudioClient, state->hFeed);
@@ -857,12 +862,21 @@ int wasapi_validate_device(struct mp_log *log, const m_option_t *opt,
HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
HRESULT hr;
+
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+
hr = CoGetInterfaceAndReleaseStream(state->sAudioClient,
&IID_IAudioClient,
(void**) &state->pAudioClientProxy);
state->sAudioClient = NULL;
EXIT_ON_ERROR(hr);
+ hr = CoGetInterfaceAndReleaseStream(state->sAudioVolume,
+ &IID_ISimpleAudioVolume,
+ (void**) &state->pAudioVolumeProxy);
+ state->sAudioVolume = NULL;
+ EXIT_ON_ERROR(hr);
+
hr = CoGetInterfaceAndReleaseStream(state->sEndpointVolume,
&IID_IAudioEndpointVolume,
(void**) &state->pEndpointVolumeProxy);
@@ -870,12 +884,18 @@ HRESULT wasapi_setup_proxies(struct wasapi_state *state) {
EXIT_ON_ERROR(hr);
exit_label:
+ if (hr != S_OK) {
+ MP_ERR(state, "error reading COM proxy: %08x %s\n", hr, wasapi_explain_err(hr));
+ }
return hr;
}
void wasapi_release_proxies(wasapi_state *state) {
SAFE_RELEASE(state->pAudioClientProxy, IUnknown_Release(state->pAudioClientProxy));
+ SAFE_RELEASE(state->pAudioVolumeProxy, IUnknown_Release(state->pAudioVolumeProxy));
SAFE_RELEASE(state->pEndpointVolumeProxy, IUnknown_Release(state->pEndpointVolumeProxy));
+
+ CoUninitialize();
}
static HRESULT create_proxies(struct wasapi_state *state) {
@@ -888,6 +908,13 @@ static HRESULT create_proxies(struct wasapi_state *state) {
&state->sAudioClient);
EXIT_ON_ERROR(hr);
+ hr = CreateStreamOnHGlobal(NULL, TRUE, &state->sAudioVolume);
+ EXIT_ON_ERROR(hr);
+ hr = CoMarshalInterThreadInterfaceInStream(&IID_ISimpleAudioVolume,
+ (IUnknown*) state->pAudioVolume,
+ &state->sAudioVolume);
+ EXIT_ON_ERROR(hr);
+
hr = CreateStreamOnHGlobal(NULL, TRUE, &state->sEndpointVolume);
EXIT_ON_ERROR(hr);
hr = CoMarshalInterThreadInterfaceInStream(&IID_IAudioEndpointVolume,
@@ -942,6 +969,16 @@ int wasapi_thread_init(struct ao *ao)
goto exit_label;
if (!fix_format(state)) { /* now that we're sure what format to use */
EXIT_ON_ERROR(create_proxies(state));
+
+ if (state->opt_exclusive)
+ IAudioEndpointVolume_GetMasterVolumeLevelScalar(state->pEndpointVolume,
+ &state->initial_volume);
+ else
+ ISimpleAudioVolume_GetMasterVolume(state->pAudioVolume,
+ &state->initial_volume);
+
+ state->previous_volume = state->initial_volume;
+
MP_VERBOSE(ao, "thread_init OK!\n");
SetEvent(state->init_done);
return state->init_ret;
@@ -956,14 +993,18 @@ void wasapi_thread_uninit(wasapi_state *state)
{
if (state->pAudioClient)
IAudioClient_Stop(state->pAudioClient);
- if (state->pRenderClient)
- IAudioRenderClient_Release(state->pRenderClient);
- if (state->pAudioClock)
- IAudioClock_Release(state->pAudioClock);
- if (state->pAudioClient)
- IAudioClient_Release(state->pAudioClient);
- if (state->pDevice)
- IMMDevice_Release(state->pDevice);
+
+ if (state->opt_exclusive)
+ IAudioEndpointVolume_SetMasterVolumeLevelScalar(state->pEndpointVolume,
+ state->initial_volume, NULL);
+
+ SAFE_RELEASE(state->pRenderClient, IAudioRenderClient_Release(state->pRenderClient));
+ SAFE_RELEASE(state->pAudioClock, IAudioClock_Release(state->pAudioClock));
+ SAFE_RELEASE(state->pAudioVolume, ISimpleAudioVolume_Release(state->pAudioVolume));
+ SAFE_RELEASE(state->pEndpointVolume, IAudioEndpointVolume_Release(state->pEndpointVolume));
+ SAFE_RELEASE(state->pAudioClient, IAudioClient_Release(state->pAudioClient));
+ SAFE_RELEASE(state->pDevice, IMMDevice_Release(state->pDevice));
+
if (state->hTask)
state->VistaBlob.pAvRevertMmThreadCharacteristics(state->hTask);
CoUninitialize();