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
This commit is contained in:
wm4 2012-01-17 07:54:11 +01:00
parent 56c1ab1c62
commit 06b1de2687
3 changed files with 65 additions and 23 deletions

View File

@ -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

74
mixer.c
View File

@ -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)

View File

@ -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;