From 06b1de26875dded600ac5771d4c0bd87f1d6e802 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:54:11 +0100 Subject: mixer, libao: add proper mute control The mixer frontend code can now make use of a proper system mixer mute toggle, if the audio output driver supports it. The consequence is that, if support is available, mplayer will no longer temporarily set the system volume to 0 if mute is enabled. Generally, the code now deals with the following combinations of available AO features: - software volume control forced by user (--softvol / soft_vol flag) => if enabled, never touch the "hardware" controls - "hardware"/driver volume control available (whether AOCONTROL_GET/SET_VOLUME works) => if not available, enable volume controls by enabling softvol - "hardware"/driver mute control (AOCONTROL_GET/SET_MUTE) => if not available, emulate by setting volume to 0 - whether the volume+mute controls are kept or not when the AO is closed (indicated by ao->no_persistent_volume) => if not persistent, restore the volume/mute next time the AO is opened --- libao2/audio_out.h | 13 ++++++++-- mixer.c | 74 ++++++++++++++++++++++++++++++++++++++---------------- mixer.h | 1 + 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/libao2/audio_out.h b/libao2/audio_out.h index a8efc92069..c5ddb5bf60 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -97,10 +97,19 @@ void list_audio_out(void); #define AOCONTROL_SET_DEVICE 1 #define AOCONTROL_GET_DEVICE 2 #define AOCONTROL_QUERY_FORMAT 3 /* test for availabilty of a format */ +// Uses ao_control_vol_t* as arg. +// If the volume controls are joint, the average of both volumes should be used. #define AOCONTROL_GET_VOLUME 4 #define AOCONTROL_SET_VOLUME 5 -#define AOCONTROL_SET_PLUGIN_DRIVER 6 -#define AOCONTROL_SET_PLUGIN_LIST 7 +// Uses ao_control_vol_t* as arg. +// left==0.0f means muted, left==1.0f means not muted (right likewise) +// Other values between are invalid. +// If the mtue controls are joint, the output should be muted if either of the +// two channels are muted. +#define AOCONTROL_GET_MUTE 6 +#define AOCONTROL_SET_MUTE 7 +#define AOCONTROL_SET_PLUGIN_DRIVER 8 +#define AOCONTROL_SET_PLUGIN_LIST 9 #define AOPLAY_FINAL_CHUNK 1 diff --git a/mixer.c b/mixer.c index 61dc1dcb13..dfa52e6b28 100644 --- a/mixer.c +++ b/mixer.c @@ -35,22 +35,31 @@ char *mixer_channel = NULL; int soft_vol = 0; float soft_vol_max = 110.0; +static void internal_setvolume(mixer_t *mixer, float l, float r); + + // Called after the audio filter chain is built or rebuilt. void mixer_reinit(mixer_t *mixer) { if (!mixer->ao) return; + // Some of this might be incorrect when the AO behavior changes (e.g. + // different AO due to file specific -ao options), but we assume this + // doesn't happen. We could attempt to handle this by trying to detect + // whether a system mixer or mute is supported, but it would probably add + // even more bugs to the code. if (mixer->restore_volume) { - int muted = mixer->muted; - mixer_setvolume(mixer, mixer->restore_vol_l, mixer->restore_vol_r); - mixer->muted = muted; + // restore previous volume (softvol, or no persistent AO volume) + internal_setvolume(mixer, mixer->restore_vol_l, mixer->restore_vol_r); } - if (mixer->muted) { - // undo mixer_uninit() - mixer_setvolume(mixer, 0, 0); - mixer->muted = true; + if (mixer->muted && + (mixer->mute_emulation || mixer->ao->no_persistent_volume)) + { + // undo mixer_uninit(), or restore mute state + mixer_setmuted(mixer, true); } if (mixer->restore_balance) { + // balance control always uses af_pan, it always needs to be restored mixer_setbalance(mixer, mixer->balance); } } @@ -62,12 +71,14 @@ void mixer_uninit(mixer_t *mixer) { if (!mixer->ao) return; - // The player is supposed to turn off the mute state when the player - // terminates. No other attempts at restoring the volume are done. + // The player is supposed to restore the volume, when mute was enabled, and + // the player terminates. No other attempts at restoring anything are done. // One complication is that the mute state should survive audio // reinitialization (e.g. when switching to a new file), so we have to be // sure mixer_reinit() will restore the mute state. - if (mixer_getmuted(mixer)) { + // This is only needed when a global system mixer without mute control is + // used, i.e. we emulate mute by setting the volume to 0. + if (mixer->mute_emulation && mixer_getmuted(mixer)) { // avoid playing the rest of the audio buffer at restored volume ao_reset(mixer->ao); mixer_setmuted(mixer, false); @@ -107,7 +118,7 @@ static float clip_vol(float v) return v > 100 ? 100 : (v < 0 ? 0 : v); } -void mixer_setvolume(mixer_t *mixer, float l, float r) +static void internal_setvolume(mixer_t *mixer, float l, float r) { l = clip_vol(l); r = clip_vol(r); @@ -156,7 +167,15 @@ void mixer_setvolume(mixer_t *mixer, float l, float r) mixer->restore_vol_r = r; } } - mixer->muted = false; +} + +void mixer_setvolume(mixer_t *mixer, float l, float r) +{ + internal_setvolume(mixer, l, r); + // Changing the volume clears mute; these are mplayer semantics. (If this + // is not desired, this would be removed, and code for restoring the softvol + // volume had to be added.) + mixer_setmuted(mixer, false); } void mixer_getvolume(mixer_t *mixer, float *l, float *r) @@ -210,9 +229,15 @@ void mixer_mute(mixer_t *mixer) bool mixer_getmuted(mixer_t *mixer) { - // this call will also check whether mute is still active, and "fix" it - float l, r; - mixer_getvolume(mixer, &l, &r); + ao_control_vol_t vol = {0}; + if (!soft_vol && + CONTROL_OK == ao_control(mixer->ao, AOCONTROL_GET_MUTE, &vol)) + { + mixer->muted = vol.left == 0.0f || vol.right == 0.0f; + } else { + float l, r; + mixer_getvolume(mixer, &l, &r); // updates mixer->muted + } return mixer->muted; } @@ -221,13 +246,20 @@ void mixer_setmuted(mixer_t *mixer, bool mute) bool muted = mixer_getmuted(mixer); if (mute == muted) return; - if (muted) { - mixer_setvolume(mixer, mixer->last_l, mixer->last_r); - } else { - mixer_getvolume(mixer, &mixer->last_l, &mixer->last_r); - mixer_setvolume(mixer, 0, 0); - mixer->muted = true; + ao_control_vol_t vol; + vol.left = vol.right = mute ? 0.0f : 1.0f; + mixer->mute_emulation = soft_vol || + CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_MUTE, &vol); + if (mixer->mute_emulation) { + // mute is emulated by setting volume to 0 + if (!mute) { + internal_setvolume(mixer, mixer->last_l, mixer->last_r); + } else { + mixer_getvolume(mixer, &mixer->last_l, &mixer->last_r); + internal_setvolume(mixer, 0, 0); + } } + mixer->muted = mute; } void mixer_getbalance(mixer_t *mixer, float *val) diff --git a/mixer.h b/mixer.h index f99e860ff3..6619783a58 100644 --- a/mixer.h +++ b/mixer.h @@ -34,6 +34,7 @@ typedef struct mixer_s { af_stream_t *afilter; int volstep; bool muted; + bool mute_emulation; float last_l, last_r; float restore_vol_l, restore_vol_r; bool restore_volume; -- cgit v1.2.3