From 670e72506a652f1876da6063b3ca0d06ec545e76 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 26 Dec 2011 01:41:35 +0100 Subject: mixer: reindent/cosmetic changes There should be no real semantic changes. Remove the mixer_setbothvolume macro, as it was unused. --- mixer.c | 227 ++++++++++++++++++++++++++++++++++------------------------------ mixer.h | 3 - 2 files changed, 119 insertions(+), 111 deletions(-) diff --git a/mixer.c b/mixer.c index d4e794d5f5..ba58ed8a85 100644 --- a/mixer.c +++ b/mixer.c @@ -30,150 +30,161 @@ #include "mixer.h" -char * mixer_device=NULL; -char * mixer_channel=NULL; +char *mixer_device = NULL; +char *mixer_channel = NULL; int soft_vol = 0; float soft_vol_max = 110.0; void mixer_getvolume(mixer_t *mixer, float *l, float *r) { - ao_control_vol_t vol; - *l=0; *r=0; - if (mixer->ao) { - if(soft_vol || - CONTROL_OK != ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol)) { - if (!mixer->afilter) - return; - else { - float db_vals[AF_NCH]; - if (!af_control_any_rev(mixer->afilter, - AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET, db_vals)) - db_vals[0] = db_vals[1] = 1.0; - else - af_from_dB (2, db_vals, db_vals, 20.0, -200.0, 60.0); - vol.left = (db_vals[0] / (soft_vol_max / 100.0)) * 100.0; - vol.right = (db_vals[1] / (soft_vol_max / 100.0)) * 100.0; - } + ao_control_vol_t vol; + *l = 0; + *r = 0; + if (mixer->ao) { + if (soft_vol || + CONTROL_OK != ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol)) + { + if (!mixer->afilter) + return; + float db_vals[AF_NCH]; + if (!af_control_any_rev(mixer->afilter, + AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET, db_vals)) + { + db_vals[0] = db_vals[1] = 1.0; + } else { + af_from_dB(2, db_vals, db_vals, 20.0, -200.0, 60.0); + } + vol.left = (db_vals[0] / (soft_vol_max / 100.0)) * 100.0; + vol.right = (db_vals[1] / (soft_vol_max / 100.0)) * 100.0; + } + *r = vol.right; + *l = vol.left; } - *r=vol.right; - *l=vol.left; - } } void mixer_setvolume(mixer_t *mixer, float l, float r) { - ao_control_vol_t vol; - vol.right=r; vol.left=l; - if (mixer->ao) { - if(soft_vol || - CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol)) { - if (!mixer->afilter) - return; - else { - // af_volume uses values in dB - float db_vals[AF_NCH]; - int i; - db_vals[0] = (l / 100.0) * (soft_vol_max / 100.0); - db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0); - for (i = 2; i < AF_NCH; i++) { - db_vals[i] = ((l + r) / 100.0) * (soft_vol_max / 100.0) / 2.0; - } - af_to_dB (AF_NCH, db_vals, db_vals, 20.0); - if (!af_control_any_rev(mixer->afilter, - AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) { - mp_tmsg(MSGT_GLOBAL, MSGL_INFO, "[Mixer] No hardware mixing, inserting volume filter.\n"); - if (af_add(mixer->afilter, "volume")) { + ao_control_vol_t vol; + vol.right = r; + vol.left = l; + if (mixer->ao) { + if (soft_vol || + CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol)) + { + if (!mixer->afilter) + return; + // af_volume uses values in dB + float db_vals[AF_NCH]; + int i; + db_vals[0] = (l / 100.0) * (soft_vol_max / 100.0); + db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0); + for (i = 2; i < AF_NCH; i++) + db_vals[i] = ((l + r) / 100.0) * (soft_vol_max / 100.0) / 2.0; + af_to_dB(AF_NCH, db_vals, db_vals, 20.0); if (!af_control_any_rev(mixer->afilter, - AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) { - mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No volume control available.\n"); - return; + AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) + { + mp_tmsg(MSGT_GLOBAL, MSGL_INFO, + "[Mixer] No hardware mixing, inserting volume filter.\n"); + if (af_add(mixer->afilter, "volume")) { + if (!af_control_any_rev(mixer->afilter, + AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) + { + mp_tmsg(MSGT_GLOBAL, MSGL_ERR, + "[Mixer] No volume control available.\n"); + return; + } + } } - } - } - } + } } - } - mixer->muted=0; + mixer->muted = 0; } void mixer_incvolume(mixer_t *mixer) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - mixer_l += mixer->volstep; - if ( mixer_l > 100 ) mixer_l = 100; - mixer_r += mixer->volstep; - if ( mixer_r > 100 ) mixer_r = 100; - mixer_setvolume(mixer, mixer_l, mixer_r); + float mixer_l, mixer_r; + mixer_getvolume(mixer, &mixer_l, &mixer_r); + mixer_l += mixer->volstep; + if (mixer_l > 100) + mixer_l = 100; + mixer_r += mixer->volstep; + if (mixer_r > 100) + mixer_r = 100; + mixer_setvolume(mixer, mixer_l, mixer_r); } void mixer_decvolume(mixer_t *mixer) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - mixer_l -= mixer->volstep; - if ( mixer_l < 0 ) mixer_l = 0; - mixer_r -= mixer->volstep; - if ( mixer_r < 0 ) mixer_r = 0; - mixer_setvolume(mixer, mixer_l, mixer_r); + float mixer_l, mixer_r; + mixer_getvolume(mixer, &mixer_l, &mixer_r); + mixer_l -= mixer->volstep; + if (mixer_l < 0) + mixer_l = 0; + mixer_r -= mixer->volstep; + if (mixer_r < 0) + mixer_r = 0; + mixer_setvolume(mixer, mixer_l, mixer_r); } void mixer_getbothvolume(mixer_t *mixer, float *b) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - *b = ( mixer_l + mixer_r ) / 2; + float mixer_l, mixer_r; + mixer_getvolume(mixer, &mixer_l, &mixer_r); + *b = (mixer_l + mixer_r) / 2; } void mixer_mute(mixer_t *mixer) { - if (mixer->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=1; - } + if (mixer->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 = 1; + } } void mixer_getbalance(mixer_t *mixer, float *val) { - *val = 0.f; - if(!mixer->afilter) - return; - af_control_any_rev(mixer->afilter, - AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET, val); + *val = 0.f; + if (!mixer->afilter) + return; + af_control_any_rev(mixer->afilter, AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET, + val); } void mixer_setbalance(mixer_t *mixer, float val) { - float level[AF_NCH]; - int i; - af_control_ext_t arg_ext = { .arg = level }; - af_instance_t* af_pan_balance; - - if(!mixer->afilter) - return; - if (af_control_any_rev(mixer->afilter, - AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) - return; - - if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { - mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No balance control available.\n"); - return; - } - - af_init(mixer->afilter); - /* make all other channels pass thru since by default pan blocks all */ - memset(level, 0, sizeof(level)); - for (i = 2; i < AF_NCH; i++) { - arg_ext.ch = i; - level[i] = 1.f; - af_pan_balance->control(af_pan_balance, - AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET, &arg_ext); - level[i] = 0.f; - } + float level[AF_NCH]; + int i; + af_control_ext_t arg_ext = { .arg = level }; + af_instance_t *af_pan_balance; - af_pan_balance->control(af_pan_balance, - AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); + if (!mixer->afilter) + return; + if (af_control_any_rev(mixer->afilter, + AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) + return; + + if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { + mp_tmsg(MSGT_GLOBAL, MSGL_ERR, + "[Mixer] No balance control available.\n"); + return; + } + + af_init(mixer->afilter); + /* make all other channels pass thru since by default pan blocks all */ + memset(level, 0, sizeof(level)); + for (i = 2; i < AF_NCH; i++) { + arg_ext.ch = i; + level[i] = 1.f; + af_pan_balance->control(af_pan_balance, + AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET, + &arg_ext); + level[i] = 0.f; + } + + af_pan_balance->control(af_pan_balance, + AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); } diff --git a/mixer.h b/mixer.h index a524c02b26..23c7f3ade0 100644 --- a/mixer.h +++ b/mixer.h @@ -44,7 +44,4 @@ void mixer_mute(mixer_t *mixer); void mixer_getbalance(mixer_t *mixer, float *bal); void mixer_setbalance(mixer_t *mixer, float bal); -//void mixer_setbothvolume(int v); -#define mixer_setbothvolume(m, v) mixer_setvolume(m, v, v) - #endif /* MPLAYER_MIXER_H */ -- cgit v1.2.3 From 685fbf25fecd13524b083f3b05264806d054f21d Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 22 Dec 2011 07:02:19 +0100 Subject: mixer: keep user volume setting when --softvol is used When --softvol is enabled, the volume set by the "volume" property is reset when changing to a new file or crossing ordered chapter boundaries. Fix this by explicitly restoring the volume on audio reinitialization. Now the behavior with --softvol should be the same as if a system mixer is used, and the volume should be persistent across file changes. This also works around an inconsistency with the mute flag. The frontend assumed the mute flag is persistent across file changes, which was not true with --softvol. If not resetting the volume on playing new files is undesired, it can be avoided by putting volume=100 in the mplayer config file. --- mixer.c | 13 +++++++++++++ mixer.h | 3 +++ mplayer.c | 1 + 3 files changed, 17 insertions(+) diff --git a/mixer.c b/mixer.c index ba58ed8a85..4c196e4bdf 100644 --- a/mixer.c +++ b/mixer.c @@ -35,6 +35,16 @@ char *mixer_channel = NULL; int soft_vol = 0; float soft_vol_max = 110.0; +// Called after the audio filter chain is built or rebuilt. +void mixer_reinit(mixer_t *mixer) +{ + if (mixer->restore_softvol) { + int muted = mixer->muted; + mixer_setvolume(mixer, mixer->softvol_l, mixer->softvol_r); + mixer->muted = muted; + } +} + void mixer_getvolume(mixer_t *mixer, float *l, float *r) { ao_control_vol_t vol; @@ -76,6 +86,9 @@ void mixer_setvolume(mixer_t *mixer, float l, float r) // af_volume uses values in dB float db_vals[AF_NCH]; int i; + mixer->softvol_l = l; + mixer->softvol_r = r; + mixer->restore_softvol = 1; db_vals[0] = (l / 100.0) * (soft_vol_max / 100.0); db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0); for (i = 2; i < AF_NCH; i++) diff --git a/mixer.h b/mixer.h index 23c7f3ade0..473933ecbe 100644 --- a/mixer.h +++ b/mixer.h @@ -33,8 +33,11 @@ typedef struct mixer_s { int volstep; int muted; float last_l, last_r; + float softvol_l, softvol_r; + int restore_softvol; } mixer_t; +void mixer_reinit(mixer_t *mixer); void mixer_getvolume(mixer_t *mixer, float *l, float *r); void mixer_setvolume(mixer_t *mixer, float l, float r); void mixer_incvolume(mixer_t *mixer); diff --git a/mplayer.c b/mplayer.c index d79d45f057..6b57c695e6 100644 --- a/mplayer.c +++ b/mplayer.c @@ -1448,6 +1448,7 @@ static int build_afilter_chain(struct MPContext *mpctx) result = init_audio_filters(sh_audio, new_srate, &ao->samplerate, &ao->channels, &ao->format); mpctx->mixer.afilter = sh_audio->afilter; + mixer_reinit(&mpctx->mixer); return result; } -- cgit v1.2.3 From 7e3a4578bd99313541c6da4060bcc0bd12fadb46 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 22 Dec 2011 07:19:43 +0100 Subject: mixer: keep balance setting Similarly to the last commit, restore the balance setting when switching to a new file. Unlike the volume setting, balance is always implemented as filter, so this affects usage without --softvol too. --- mixer.c | 7 +++++++ mixer.h | 1 + 2 files changed, 8 insertions(+) diff --git a/mixer.c b/mixer.c index 4c196e4bdf..2f3d57b07e 100644 --- a/mixer.c +++ b/mixer.c @@ -43,6 +43,9 @@ void mixer_reinit(mixer_t *mixer) mixer_setvolume(mixer, mixer->softvol_l, mixer->softvol_r); mixer->muted = muted; } + if (mixer->balance != 0) { + mixer_setbalance(mixer, mixer->balance); + } } void mixer_getvolume(mixer_t *mixer, float *l, float *r) @@ -176,6 +179,9 @@ void mixer_setbalance(mixer_t *mixer, float val) if (!mixer->afilter) return; + + mixer->balance = val; + if (af_control_any_rev(mixer->afilter, AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) return; @@ -183,6 +189,7 @@ void mixer_setbalance(mixer_t *mixer, float val) if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No balance control available.\n"); + mixer->balance = 0; return; } diff --git a/mixer.h b/mixer.h index 473933ecbe..1e45ac56b4 100644 --- a/mixer.h +++ b/mixer.h @@ -35,6 +35,7 @@ typedef struct mixer_s { float last_l, last_r; float softvol_l, softvol_r; int restore_softvol; + float balance; } mixer_t; void mixer_reinit(mixer_t *mixer); -- cgit v1.2.3 From 7eac60417f3a6714675e862a8a1612d1edf183fc Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 22 Dec 2011 08:25:41 +0100 Subject: af: print audio filter chain in verbose mode The string format used in print_fmt() is taken from init_audio_filters(). --- libaf/af.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/libaf/af.c b/libaf/af.c index 80f9871bfb..82a7d7398b 100644 --- a/libaf/af.c +++ b/libaf/af.c @@ -246,6 +246,38 @@ void af_remove(af_stream_t* s, af_instance_t* af) free(af); } +static void print_fmt(af_data_t *d) +{ + if (d) { + mp_msg(MSGT_AFILTER, MSGL_V, "%dHz/%dch/%s", d->rate, d->nch, + af_fmt2str_short(d->format)); + } else { + mp_msg(MSGT_AFILTER, MSGL_V, "(?)"); + } +} + +static void af_print_filter_chain(af_stream_t* s) +{ + mp_msg(MSGT_AFILTER, MSGL_V, "Audio filter chain:\n"); + + mp_msg(MSGT_AFILTER, MSGL_V, " [in] "); + print_fmt(&s->input); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + + af_instance_t *af = s->first; + while (af) { + mp_msg(MSGT_AFILTER, MSGL_V, " [%s] ", af->info->name); + print_fmt(af->data); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); + + af = af->next; + } + + mp_msg(MSGT_AFILTER, MSGL_V, " [out] "); + print_fmt(&s->output); + mp_msg(MSGT_AFILTER, MSGL_V, "\n"); +} + int af_reinit(af_stream_t* s, af_instance_t* af) { do{ @@ -343,6 +375,9 @@ int af_reinit(af_stream_t* s, af_instance_t* af) return AF_ERROR; } }while(af); + + af_print_filter_chain(s); + return AF_OK; } -- cgit v1.2.3 From d1b38a8a371d13ce59b2d1b4152f0252ac8d8b4c Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 26 Dec 2011 02:23:51 +0100 Subject: audio: fix crash when exiting file when mute is enabled --- mplayer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/mplayer.c b/mplayer.c index 6b57c695e6..8f635576c3 100644 --- a/mplayer.c +++ b/mplayer.c @@ -719,6 +719,7 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) if (mpctx->ao) ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE); mpctx->ao = NULL; + mpctx->mixer.ao = NULL; } current_module = NULL; -- cgit v1.2.3 From 95c52f865da2459f47ed708c41324f0cc5386da3 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 28 Dec 2011 00:06:23 +0100 Subject: softvol: make sure softvol settings are restored when switching audio tracks At least in the case when switching to no audio track and then switching back, the volume settings were not restored with --softvol. Fix this by moving the call restoring the settings to a better place. --- mplayer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mplayer.c b/mplayer.c index 8f635576c3..52889c7d58 100644 --- a/mplayer.c +++ b/mplayer.c @@ -1449,7 +1449,6 @@ static int build_afilter_chain(struct MPContext *mpctx) result = init_audio_filters(sh_audio, new_srate, &ao->samplerate, &ao->channels, &ao->format); mpctx->mixer.afilter = sh_audio->afilter; - mixer_reinit(&mpctx->mixer); return result; } @@ -1854,6 +1853,7 @@ void reinit_audio_chain(struct MPContext *mpctx) } mpctx->mixer.ao = ao; mpctx->mixer.volstep = volstep; + mixer_reinit(&mpctx->mixer); mpctx->syncing_audio = true; return; -- cgit v1.2.3 From 29effe9593465f9096227edb79e09e51ca8a054d Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 6 Jan 2012 16:23:43 +0100 Subject: mixer: restore volume with audio output drivers without persistent volume Some audio outputs don't provide access to a system-wide mixer control, and do per-application audio mixing. Further, some of these forget the volume as soon as the audio device is closed. This can be annoying, because mplayer will "forget" the volume when playing a new file or when crossing ordered chapter boundaries. Support restoring the volume on audio reinitialization if an audio output driver knowingly behaves this way. (This doesn't change that mplayer never writes any settings into the config file, including volume settings.) This commit doesn't yet change any actual output driver to use this code. Hopefully make some logic in the volume restore code a bit more robust. --- libao2/audio_out.h | 1 + mixer.c | 47 +++++++++++++++++++++++++++++------------------ mixer.h | 7 +++++-- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/libao2/audio_out.h b/libao2/audio_out.h index e96e123700..a8efc92069 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -78,6 +78,7 @@ struct ao { int buffer_playable_size; bool initialized; bool untimed; + bool no_persistent_volume; const struct ao_driver *driver; void *priv; }; diff --git a/mixer.c b/mixer.c index 2f3d57b07e..5cd1b722d9 100644 --- a/mixer.c +++ b/mixer.c @@ -38,12 +38,14 @@ float soft_vol_max = 110.0; // Called after the audio filter chain is built or rebuilt. void mixer_reinit(mixer_t *mixer) { - if (mixer->restore_softvol) { + if (!mixer->ao) + return; + if (mixer->restore_volume) { int muted = mixer->muted; - mixer_setvolume(mixer, mixer->softvol_l, mixer->softvol_r); + mixer_setvolume(mixer, mixer->restore_vol_l, mixer->restore_vol_r); mixer->muted = muted; } - if (mixer->balance != 0) { + if (mixer->restore_balance) { mixer_setbalance(mixer, mixer->balance); } } @@ -81,17 +83,21 @@ void mixer_setvolume(mixer_t *mixer, float l, float r) vol.right = r; vol.left = l; if (mixer->ao) { - if (soft_vol || - CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol)) - { + bool use_softvol = soft_vol; + if (!use_softvol) { + if (CONTROL_OK != ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol)) + { + use_softvol = true; + } else { + mixer->restore_volume = mixer->ao->no_persistent_volume; + } + } + if (use_softvol) { if (!mixer->afilter) return; // af_volume uses values in dB float db_vals[AF_NCH]; int i; - mixer->softvol_l = l; - mixer->softvol_r = r; - mixer->restore_softvol = 1; db_vals[0] = (l / 100.0) * (soft_vol_max / 100.0); db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0); for (i = 2; i < AF_NCH; i++) @@ -102,16 +108,20 @@ void mixer_setvolume(mixer_t *mixer, float l, float r) { mp_tmsg(MSGT_GLOBAL, MSGL_INFO, "[Mixer] No hardware mixing, inserting volume filter.\n"); - if (af_add(mixer->afilter, "volume")) { - if (!af_control_any_rev(mixer->afilter, - AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals)) - { - mp_tmsg(MSGT_GLOBAL, MSGL_ERR, - "[Mixer] No volume control available.\n"); - return; - } + if (!(af_add(mixer->afilter, "volume") + && af_control_any_rev(mixer->afilter, + AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, db_vals))) + { + mp_tmsg(MSGT_GLOBAL, MSGL_ERR, + "[Mixer] No volume control available.\n"); + return; } } + mixer->restore_volume = true; + } + if (mixer->restore_volume) { + mixer->restore_vol_l = l; + mixer->restore_vol_r = r; } } mixer->muted = 0; @@ -181,6 +191,7 @@ void mixer_setbalance(mixer_t *mixer, float val) return; mixer->balance = val; + mixer->restore_balance = true; if (af_control_any_rev(mixer->afilter, AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) @@ -189,7 +200,7 @@ void mixer_setbalance(mixer_t *mixer, float val) if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No balance control available.\n"); - mixer->balance = 0; + mixer->restore_balance = false; return; } diff --git a/mixer.h b/mixer.h index 1e45ac56b4..5aa60d67d1 100644 --- a/mixer.h +++ b/mixer.h @@ -19,6 +19,8 @@ #ifndef MPLAYER_MIXER_H #define MPLAYER_MIXER_H +#include + #include "libaf/af.h" #include "libao2/audio_out.h" @@ -33,9 +35,10 @@ typedef struct mixer_s { int volstep; int muted; float last_l, last_r; - float softvol_l, softvol_r; - int restore_softvol; + float restore_vol_l, restore_vol_r; + bool restore_volume; float balance; + bool restore_balance; } mixer_t; void mixer_reinit(mixer_t *mixer); -- cgit v1.2.3 From 15a318b2a56766e7bc2dee5d4cc8f514ba21fe39 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 6 Jan 2012 16:25:52 +0100 Subject: ao_coreaudio: signal that volume controls are not persistent This will make the mplayer frontend restore the volume on its own when the audio device is reinitialized. --- libao2/ao_coreaudio.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libao2/ao_coreaudio.c b/libao2/ao_coreaudio.c index 34374f4c9c..50e943e9ac 100644 --- a/libao2/ao_coreaudio.c +++ b/libao2/ao_coreaudio.c @@ -450,6 +450,8 @@ int device_id, display_help = 0; ao->b_revert = 0; ao->b_changed_mixing = 0; + global_ao->no_persistent_volume = true; + if (device_id == 0) { /* Find the ID of the default Device. */ err = GetAudioProperty(kAudioObjectSystemObject, -- cgit v1.2.3 From aae97b7e254f312e5c7bfbe940cb8515a25bf11d Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 7 Jan 2012 14:10:05 +0100 Subject: audio: properly restore audio volume on exit when mute is used When you mute audio, mplayer is supposed to restore the volume controls on exit. This affects when --softvol isn't used and the audio output driver volume controls directly affect the system wide volume controls. This wasn't done in some cases. --- command.c | 3 +-- mixer.c | 36 ++++++++++++++++++++++++++++++++++-- mixer.h | 2 ++ mplayer.c | 14 +++++--------- 4 files changed, 42 insertions(+), 13 deletions(-) diff --git a/command.c b/command.c index be1d688f8f..aee38463fd 100644 --- a/command.c +++ b/command.c @@ -762,8 +762,7 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, return M_PROPERTY_DISABLED; if (!arg) return M_PROPERTY_ERROR; - if ((!!*(int *) arg) != mpctx->mixer.muted) - mixer_mute(&mpctx->mixer); + mixer_setmuted(&mpctx->mixer, *(int *) arg); mpctx->user_muted = mpctx->mixer.muted; return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: diff --git a/mixer.c b/mixer.c index 5cd1b722d9..d0e6423e6f 100644 --- a/mixer.c +++ b/mixer.c @@ -45,11 +45,36 @@ void mixer_reinit(mixer_t *mixer) mixer_setvolume(mixer, mixer->restore_vol_l, mixer->restore_vol_r); mixer->muted = muted; } + if (mixer->muted) { + // undo mixer_uninit() + mixer_setvolume(mixer, 0, 0); + mixer->muted = true; + } if (mixer->restore_balance) { mixer_setbalance(mixer, mixer->balance); } } +// Called before the audio output is uninitialized. +// Note that this doesn't necessarily terminate the mixer_t instance, and it's +// possible that mixer_reinit() will be called later. +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. + // 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->muted) { + // avoid playing the rest of the audio buffer at restored volume + ao_reset(mixer->ao); + mixer_setmuted(mixer, false); + mixer->muted = true; + } +} + void mixer_getvolume(mixer_t *mixer, float *l, float *r) { ao_control_vol_t vol; @@ -124,7 +149,7 @@ void mixer_setvolume(mixer_t *mixer, float l, float r) mixer->restore_vol_r = r; } } - mixer->muted = 0; + mixer->muted = false; } void mixer_incvolume(mixer_t *mixer) @@ -162,12 +187,19 @@ void mixer_getbothvolume(mixer_t *mixer, float *b) void mixer_mute(mixer_t *mixer) { + mixer_setmuted(mixer, !mixer->muted); +} + +void mixer_setmuted(mixer_t *mixer, bool mute) +{ + if (mute == mixer->muted) + return; if (mixer->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 = 1; + mixer->muted = true; } } diff --git a/mixer.h b/mixer.h index 5aa60d67d1..f05688bc72 100644 --- a/mixer.h +++ b/mixer.h @@ -42,12 +42,14 @@ typedef struct mixer_s { } mixer_t; void mixer_reinit(mixer_t *mixer); +void mixer_uninit(mixer_t *mixer); void mixer_getvolume(mixer_t *mixer, float *l, float *r); void mixer_setvolume(mixer_t *mixer, float l, float r); void mixer_incvolume(mixer_t *mixer); void mixer_decvolume(mixer_t *mixer); void mixer_getbothvolume(mixer_t *mixer, float *b); void mixer_mute(mixer_t *mixer); +void mixer_setmuted(mixer_t *mixer, bool mute); void mixer_getbalance(mixer_t *mixer, float *bal); void mixer_setbalance(mixer_t *mixer, float bal); diff --git a/mplayer.c b/mplayer.c index 52889c7d58..fb1638bbd0 100644 --- a/mplayer.c +++ b/mplayer.c @@ -714,10 +714,10 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) if (mask & INITIALIZED_AO) { mpctx->initialized_flags &= ~INITIALIZED_AO; current_module = "uninit_ao"; - if (mpctx->edl_muted) - mixer_mute(&mpctx->mixer); - if (mpctx->ao) + if (mpctx->ao) { + mixer_uninit(&mpctx->mixer); ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE); + } mpctx->ao = NULL; mpctx->mixer.ao = NULL; } @@ -727,8 +727,6 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) void exit_player_with_rc(struct MPContext *mpctx, enum exit_reason how, int rc) { - if (mpctx->user_muted && !mpctx->edl_muted) - mixer_mute(&mpctx->mixer); uninit_player(mpctx, INITIALIZED_ALL); #if defined(__MINGW32__) || defined(__CYGWIN__) timeEndPeriod(1); @@ -3124,8 +3122,7 @@ static void edl_seek_reset(MPContext *mpctx) mpctx->edl_muted = !mpctx->edl_muted; next_edl_record = next_edl_record->next; } - if ((mpctx->user_muted | mpctx->edl_muted) != mpctx->mixer.muted) - mixer_mute(&mpctx->mixer); + mixer_setmuted(&mpctx->mixer, mpctx->edl_muted || mpctx->user_muted); } @@ -3153,8 +3150,7 @@ static void edl_update(MPContext *mpctx) next_edl_record->stop_sec, next_edl_record->length_sec); } else if (next_edl_record->action == EDL_MUTE) { mpctx->edl_muted = !mpctx->edl_muted; - if ((mpctx->user_muted | mpctx->edl_muted) != mpctx->mixer.muted) - mixer_mute(&mpctx->mixer); + mixer_setmuted(&mpctx->mixer, mpctx->edl_muted || mpctx->user_muted); mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_MUTE: [%f]\n", next_edl_record->start_sec); } -- cgit v1.2.3 From 6afaf948cd80e348c4ff93f44baae362d4423793 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 7 Jan 2012 17:33:40 +0100 Subject: audio: pretend muting doesn't set volume to 0 Muting audio is implemented by setting the volume controls to 0. This has the annoying consequence that attempting to change the volume while the audio is muted will reset the user's volume setting. E.g. increasing the volume while muted will start from 0, instead from the volume that was set before muting. Changing the volume while muted effectively resets the volume to 0 (which is not very useful), with no possibility of restoring the old voume. This commit makes mplayer always report the volume that was set when mute was enabled while mute is still active. Caveat: this might be have confusing effects when the volume control is directly connected with a system wide mixer setting. Now it's even less obvious (and thus more confusing) that muting will set the mixer volume to 0. Also always clip input volumes, and remove some minor code duplication. --- mixer.c | 48 ++++++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/mixer.c b/mixer.c index d0e6423e6f..4b99a6e967 100644 --- a/mixer.c +++ b/mixer.c @@ -75,7 +75,7 @@ void mixer_uninit(mixer_t *mixer) } } -void mixer_getvolume(mixer_t *mixer, float *l, float *r) +static void internal_getvolume(mixer_t *mixer, float *l, float *r) { ao_control_vol_t vol; *l = 0; @@ -102,8 +102,15 @@ void mixer_getvolume(mixer_t *mixer, float *l, float *r) } } +static float clip_vol(float v) +{ + return v > 100 ? 100 : (v < 0 ? 0 : v); +} + void mixer_setvolume(mixer_t *mixer, float l, float r) { + l = clip_vol(l); + r = clip_vol(r); ao_control_vol_t vol; vol.right = r; vol.left = l; @@ -152,30 +159,35 @@ void mixer_setvolume(mixer_t *mixer, float l, float r) mixer->muted = false; } -void mixer_incvolume(mixer_t *mixer) +void mixer_getvolume(mixer_t *mixer, float *l, float *r) +{ + *l = 0; + *r = 0; + if (mixer->ao) { + if (mixer->muted) { + *l = mixer->last_l; + *r = mixer->last_r; + } else { + internal_getvolume(mixer, l, r); + } + } +} + +static void mixer_addvolume(mixer_t *mixer, float d) { float mixer_l, mixer_r; mixer_getvolume(mixer, &mixer_l, &mixer_r); - mixer_l += mixer->volstep; - if (mixer_l > 100) - mixer_l = 100; - mixer_r += mixer->volstep; - if (mixer_r > 100) - mixer_r = 100; - mixer_setvolume(mixer, mixer_l, mixer_r); + mixer_setvolume(mixer, mixer_l + d, mixer_r + d); +} + +void mixer_incvolume(mixer_t *mixer) +{ + mixer_addvolume(mixer, +mixer->volstep); } void mixer_decvolume(mixer_t *mixer) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - mixer_l -= mixer->volstep; - if (mixer_l < 0) - mixer_l = 0; - mixer_r -= mixer->volstep; - if (mixer_r < 0) - mixer_r = 0; - mixer_setvolume(mixer, mixer_l, mixer_r); + mixer_addvolume(mixer, -mixer->volstep); } void mixer_getbothvolume(mixer_t *mixer, float *b) -- cgit v1.2.3 From b338b16be7fe902723fc9c5c20f88959264e67d7 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 7 Jan 2012 18:06:30 +0100 Subject: audio: reset mplayer's mute state when the system mixer volume changes Before this commit, the mute state was only reset when either mute was explicitly cleared, or the volume was changed via mplayer controls. If the volume controls are connected to the system mixer, and the system mixer volume is changed otherwise (e.g. with alsamixer), the mute setting was inconsistent. Avoid this by checking the volume. If the returned volume is not 0, the mute flag is considered invalid. This relies on system mixers always returning a volume of 0 when mplayer has set the volume 0. Possible caveat: if the audio output's volume control don't return a volume of exactly 0 after 0 was written, enabling mute basically won't work. It will set the volume to silence, forget the previous volume, and report that mute is disabled. --- command.c | 7 ++++--- mixer.c | 25 ++++++++++++++++++++----- mixer.h | 3 ++- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/command.c b/command.c index aee38463fd..414fd1e33b 100644 --- a/command.c +++ b/command.c @@ -763,14 +763,14 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, if (!arg) return M_PROPERTY_ERROR; mixer_setmuted(&mpctx->mixer, *(int *) arg); - mpctx->user_muted = mpctx->mixer.muted; + mpctx->user_muted = mixer_getmuted(&mpctx->mixer); return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: if (mpctx->edl_muted) return M_PROPERTY_DISABLED; mixer_mute(&mpctx->mixer); - mpctx->user_muted = mpctx->mixer.muted; + mpctx->user_muted = mixer_getmuted(&mpctx->mixer); return M_PROPERTY_OK; case M_PROPERTY_PRINT: if (!arg) @@ -780,7 +780,8 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, return M_PROPERTY_OK; } default: - return m_property_flag(prop, action, arg, &mpctx->mixer.muted); + return m_property_flag_ro(prop, action, arg, + mixer_getmuted(&mpctx->mixer)); } } diff --git a/mixer.c b/mixer.c index 4b99a6e967..61dc1dcb13 100644 --- a/mixer.c +++ b/mixer.c @@ -67,7 +67,7 @@ void mixer_uninit(mixer_t *mixer) // 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->muted) { + if (mixer_getmuted(mixer)) { // avoid playing the rest of the audio buffer at restored volume ao_reset(mixer->ao); mixer_setmuted(mixer, false); @@ -164,11 +164,17 @@ void mixer_getvolume(mixer_t *mixer, float *l, float *r) *l = 0; *r = 0; if (mixer->ao) { + float real_l, real_r; + internal_getvolume(mixer, &real_l, &real_r); + // consider the case when the system mixer volumes change independently + if (real_l != 0 || real_r != 0) + mixer->muted = false; if (mixer->muted) { *l = mixer->last_l; *r = mixer->last_r; } else { - internal_getvolume(mixer, l, r); + *l = real_l; + *r = real_r; } } } @@ -199,14 +205,23 @@ void mixer_getbothvolume(mixer_t *mixer, float *b) void mixer_mute(mixer_t *mixer) { - mixer_setmuted(mixer, !mixer->muted); + mixer_setmuted(mixer, !mixer_getmuted(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); + return mixer->muted; } void mixer_setmuted(mixer_t *mixer, bool mute) { - if (mute == mixer->muted) + bool muted = mixer_getmuted(mixer); + if (mute == muted) return; - if (mixer->muted) { + if (muted) { mixer_setvolume(mixer, mixer->last_l, mixer->last_r); } else { mixer_getvolume(mixer, &mixer->last_l, &mixer->last_r); diff --git a/mixer.h b/mixer.h index f05688bc72..f99e860ff3 100644 --- a/mixer.h +++ b/mixer.h @@ -33,7 +33,7 @@ typedef struct mixer_s { struct ao *ao; af_stream_t *afilter; int volstep; - int muted; + bool muted; float last_l, last_r; float restore_vol_l, restore_vol_r; bool restore_volume; @@ -49,6 +49,7 @@ void mixer_incvolume(mixer_t *mixer); void mixer_decvolume(mixer_t *mixer); void mixer_getbothvolume(mixer_t *mixer, float *b); void mixer_mute(mixer_t *mixer); +bool mixer_getmuted(mixer_t *mixer); void mixer_setmuted(mixer_t *mixer, bool mute); void mixer_getbalance(mixer_t *mixer, float *bal); void mixer_setbalance(mixer_t *mixer, float bal); -- cgit v1.2.3 From ad18a33f5876be0c791bc394348a8333c50317b5 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 7 Jan 2012 18:16:12 +0100 Subject: core: remove EDL muting I'm not sure what's the point of this feature. Aside from that, the EDL code is relatively buggy anyway, and I see no reason why such an obscure feature should be left in, if it possibly causes bugs. --- command.c | 17 ----------------- mp_core.h | 3 --- mplayer.c | 25 +------------------------ 3 files changed, 1 insertion(+), 44 deletions(-) diff --git a/command.c b/command.c index 414fd1e33b..1234c0847d 100644 --- a/command.c +++ b/command.c @@ -721,10 +721,6 @@ static int mp_property_volume(m_option_t *prop, int action, void *arg, return M_PROPERTY_NOT_IMPLEMENTED; } - if (mpctx->edl_muted) - return M_PROPERTY_DISABLED; - mpctx->user_muted = 0; - switch (action) { case M_PROPERTY_SET: if (!arg) @@ -758,27 +754,14 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, switch (action) { case M_PROPERTY_SET: - if (mpctx->edl_muted) - return M_PROPERTY_DISABLED; if (!arg) return M_PROPERTY_ERROR; mixer_setmuted(&mpctx->mixer, *(int *) arg); - mpctx->user_muted = mixer_getmuted(&mpctx->mixer); return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: - if (mpctx->edl_muted) - return M_PROPERTY_DISABLED; mixer_mute(&mpctx->mixer); - mpctx->user_muted = mixer_getmuted(&mpctx->mixer); return M_PROPERTY_OK; - case M_PROPERTY_PRINT: - if (!arg) - return M_PROPERTY_ERROR; - if (mpctx->edl_muted) { - *(char **) arg = talloc_strdup(NULL, mp_gtext("enabled (EDL)")); - return M_PROPERTY_OK; - } default: return m_property_flag_ro(prop, action, arg, mixer_getmuted(&mpctx->mixer)); diff --git a/mp_core.h b/mp_core.h index 8b36465c5f..cef398dea2 100644 --- a/mp_core.h +++ b/mp_core.h @@ -187,9 +187,6 @@ typedef struct MPContext { double last_chapter_pts; float begin_skip; ///< start time of the current skip while on edlout mode - // audio is muted if either EDL or user activates mute - short edl_muted; ///< Stores whether EDL is currently in muted mode. - short user_muted; ///< Stores whether user wanted muted mode. int global_sub_size; // this encompasses all subtitle sources int global_sub_pos; // this encompasses all subtitle sources diff --git a/mplayer.c b/mplayer.c index fb1638bbd0..b33c36af00 100644 --- a/mplayer.c +++ b/mplayer.c @@ -3107,25 +3107,6 @@ static void pause_loop(struct MPContext *mpctx) } } - -// Find the right mute status and record position for new file position -static void edl_seek_reset(MPContext *mpctx) -{ - mpctx->edl_muted = 0; - next_edl_record = edl_records; - - while (next_edl_record) { - if (next_edl_record->start_sec >= get_current_time(mpctx)) - break; - - if (next_edl_record->action == EDL_MUTE) - mpctx->edl_muted = !mpctx->edl_muted; - next_edl_record = next_edl_record->next; - } - mixer_setmuted(&mpctx->mixer, mpctx->edl_muted || mpctx->user_muted); -} - - // Execute EDL command for the current position if one exists static void edl_update(MPContext *mpctx) { @@ -3149,9 +3130,7 @@ static void edl_update(MPContext *mpctx) "[%f], length [%f]\n", next_edl_record->start_sec, next_edl_record->stop_sec, next_edl_record->length_sec); } else if (next_edl_record->action == EDL_MUTE) { - mpctx->edl_muted = !mpctx->edl_muted; - mixer_setmuted(&mpctx->mixer, mpctx->edl_muted || mpctx->user_muted); - mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_MUTE: [%f]\n", + mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_MUTE: [%f] ignored\n", next_edl_record->start_sec); } next_edl_record = next_edl_record->next; @@ -3205,8 +3184,6 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao) vobsub_seek(vo_vobsub, mpctx->sh_video->pts); } - edl_seek_reset(mpctx); - mpctx->hrseek_active = false; mpctx->hrseek_framedrop = false; mpctx->total_avsync_change = 0; -- cgit v1.2.3 From 7187b4914838a302df78aa16c7a80b498f2be51a Mon Sep 17 00:00:00 2001 From: wm4 Date: Sat, 14 Jan 2012 09:06:16 +0100 Subject: af: fix crash when trying to use volume controls with AC3 pass-through Changing the volume when softvol is enabled or if the audio output driver doesn't support volume controls causes insertion of the "volume" filter. This fails with AC3. Since the filter wasn't removed after that, and the filter chain was in a bogus state, random crashes occured past this point. Fix it by reinitializing the filter chain completely on failure. Volume controls simply won't work. (This can't be fixed, because AC3 is a compressed format, and would require additional decoding/encoding passes in order to support arbitrary volume changes.) This also affects balance controls. --- libaf/af.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libaf/af.c b/libaf/af.c index 82a7d7398b..e4015727ac 100644 --- a/libaf/af.c +++ b/libaf/af.c @@ -278,6 +278,11 @@ static void af_print_filter_chain(af_stream_t* s) mp_msg(MSGT_AFILTER, MSGL_V, "\n"); } +// Warning: +// A failed af_reinit() leaves the audio chain behind in a useless, broken +// state (for example, format filters that were tentatively inserted stay +// inserted). +// In that case, you should always rebuild the filter chain, or abort. int af_reinit(af_stream_t* s, af_instance_t* af) { do{ @@ -587,7 +592,9 @@ af_instance_t* af_add(af_stream_t* s, char* name){ // Reinitalize the filter list if(AF_OK != af_reinit(s, s->first) || AF_OK != fixup_output_format(s)){ - free(new); + while (s->first) + af_remove(s, s->first); + af_init(s); return NULL; } return new; -- cgit v1.2.3 From 56c1ab1c62e4e35c7b62d2b8143f6e9247f4c96c Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 15 Jan 2012 21:01:44 +0100 Subject: libao2: try to identify some audio outputs that have persistent volume The mplayer frontend (specifically, mixer.c) needs to know this. If the audio output doesn't remember the volume across reinitialization, the frontend will restore the volume settings. There is also the assumption that the volume setting isn't global in this case (i.e. changing it won't change the volume of other applications or annoy the user otherwise). None of these changes have been tested. I'm guessing that ESD and NAS do per-connection non-persistent volume settings. --- libao2/ao_dsound.c | 2 ++ libao2/ao_esd.c | 2 ++ libao2/ao_nas.c | 2 ++ libao2/ao_openal.c | 1 + libao2/ao_sdl.c | 2 ++ 5 files changed, 9 insertions(+) diff --git a/libao2/ao_dsound.c b/libao2/ao_dsound.c index 605b0f16e0..a7d30ec357 100644 --- a/libao2/ao_dsound.c +++ b/libao2/ao_dsound.c @@ -418,6 +418,8 @@ static int init(int rate, int channels, int format, int flags) int res; if (!InitDirectSound()) return 0; + global_ao->no_persistent_volume = true; + // ok, now create the buffers WAVEFORMATEXTENSIBLE wformat; DSBUFFERDESC dsbpridesc; diff --git a/libao2/ao_esd.c b/libao2/ao_esd.c index d5423991bd..e7c6701aa0 100644 --- a/libao2/ao_esd.c +++ b/libao2/ao_esd.c @@ -164,6 +164,8 @@ static int init(int rate_hz, int channels, int format, int flags) float lag_seconds, lag_net = 0., lag_serv; struct timeval proto_start, proto_end; + global_ao->no_persistent_volume = true; + if (esd_fd < 0) { esd_fd = esd_open_sound(server); if (esd_fd < 0) { diff --git a/libao2/ao_nas.c b/libao2/ao_nas.c index fb49c5e60e..d3274df9a5 100644 --- a/libao2/ao_nas.c +++ b/libao2/ao_nas.c @@ -424,6 +424,8 @@ static int init(int rate,int channels,int format,int flags) (void)flags; /* shut up 'unused parameter' warning */ + global_ao->no_persistent_volume = true; + nas_data=malloc(sizeof(struct ao_nas_data)); memset(nas_data, 0, sizeof(struct ao_nas_data)); diff --git a/libao2/ao_openal.c b/libao2/ao_openal.c index e425b5769c..490aac0eb8 100644 --- a/libao2/ao_openal.c +++ b/libao2/ao_openal.c @@ -108,6 +108,7 @@ static int init(int rate, int channels, int format, int flags) { const opt_t subopts[] = { {NULL} }; + global_ao->no_persistent_volume = true; if (subopt_parse(ao_subdevice, subopts) != 0) { print_help(); return 0; diff --git a/libao2/ao_sdl.c b/libao2/ao_sdl.c index e9ae7298d5..6ff8b83cb3 100644 --- a/libao2/ao_sdl.c +++ b/libao2/ao_sdl.c @@ -135,6 +135,8 @@ static int init(int rate,int channels,int format,int flags){ /* SDL Audio Specifications */ SDL_AudioSpec aspec, obtained; + global_ao->no_persistent_volume = true; + /* Allocate ring-buffer memory */ buffer = av_fifo_alloc(BUFFSIZE); -- cgit v1.2.3 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 From d4cf8cd7a2a991d05b8bc7229f059730ff78c434 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:51:44 +0100 Subject: ao_alsa: use "Master" mixer channel instead of "PCM" by default Do this, because the "Master" channel normally provides proper mute control. The old default can be forced with: --mixer-channel=PCM --- libao2/ao_alsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c index 1806a36d56..c8ac966a04 100644 --- a/libao2/ao_alsa.c +++ b/libao2/ao_alsa.c @@ -116,7 +116,7 @@ static int control(int cmd, void *arg) snd_mixer_elem_t *elem; snd_mixer_selem_id_t *sid; - char *mix_name = "PCM"; + char *mix_name = "Master"; char *card = "default"; int mix_index = 0; -- cgit v1.2.3 From 08978c5fb1776843a794f985316ec7a02044fcb4 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:54:27 +0100 Subject: ao_alsa: add mute control --- libao2/ao_alsa.c | 66 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 23 deletions(-) diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c index c8ac966a04..61837066d5 100644 --- a/libao2/ao_alsa.c +++ b/libao2/ao_alsa.c @@ -106,6 +106,8 @@ static int control(int cmd, void *arg) switch(cmd) { case AOCONTROL_QUERY_FORMAT: return CONTROL_TRUE; + case AOCONTROL_GET_MUTE: + case AOCONTROL_SET_MUTE: case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { @@ -192,16 +194,15 @@ static int control(int cmd, void *arg) snd_mixer_selem_get_playback_volume_range(elem,&pmin,&pmax); f_multi = (100 / (float)(pmax - pmin)); - if (cmd == AOCONTROL_SET_VOLUME) { - + switch (cmd) { + case AOCONTROL_SET_VOLUME: { set_vol = vol->left / f_multi + pmin + 0.5; //setting channels if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, set_vol)) < 0) { mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting left channel, %s\n", snd_strerror(err)); - snd_mixer_close(handle); - return CONTROL_ERROR; + goto mixer_error; } mp_msg(MSGT_AO,MSGL_DBG2,"left=%li, ", set_vol); @@ -210,33 +211,52 @@ static int control(int cmd, void *arg) if ((err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, set_vol)) < 0) { mp_tmsg(MSGT_AO,MSGL_ERR,"[AO_ALSA] Error setting right channel, %s\n", snd_strerror(err)); - snd_mixer_close(handle); - return CONTROL_ERROR; + goto mixer_error; } mp_msg(MSGT_AO,MSGL_DBG2,"right=%li, pmin=%li, pmax=%li, mult=%f\n", set_vol, pmin, pmax, f_multi); - - if (snd_mixer_selem_has_playback_switch(elem)) { - int lmute = (vol->left == 0.0); - int rmute = (vol->right == 0.0); - if (snd_mixer_selem_has_playback_switch_joined(elem)) { - lmute = rmute = lmute && rmute; - } else { - snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, !rmute); - } - snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !lmute); + break; + } + case AOCONTROL_GET_VOLUME: { + snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); + vol->left = (get_vol - pmin) * f_multi; + snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); + vol->right = (get_vol - pmin) * f_multi; + mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right); + break; + } + case AOCONTROL_SET_MUTE: { + if (!snd_mixer_selem_has_playback_switch(elem)) + goto mixer_error; + bool m_l = vol->left == 0.0f, m_r = vol->right == 0.0f; + if (snd_mixer_selem_has_playback_switch_joined(elem)) { + m_l = m_l || m_r; + } else { + snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, !m_r); } + snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !m_l); + break; + } + case AOCONTROL_GET_MUTE: { + if (!snd_mixer_selem_has_playback_switch(elem)) + goto mixer_error; + int tmp = 1; + snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &tmp); + vol->left = tmp ? 1.0f : 0.0f; + if (snd_mixer_selem_has_playback_switch_joined(elem)) { + vol->right = vol->left; + } else { + snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); + vol->right = tmp ? 1.0f : 0.0f; + } + break; } - else { - snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &get_vol); - vol->left = (get_vol - pmin) * f_multi; - snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, &get_vol); - vol->right = (get_vol - pmin) * f_multi; - - mp_msg(MSGT_AO,MSGL_DBG2,"left=%f, right=%f\n",vol->left,vol->right); } snd_mixer_close(handle); return CONTROL_OK; + mixer_error: + snd_mixer_close(handle); + return CONTROL_ERROR; } } //end switch -- cgit v1.2.3 From 1d60badc3d4514da49499d177cb7b8b844839086 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:55:04 +0100 Subject: ao_pulse: add mute control --- libao2/ao_pulse.c | 55 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index a07bf31da0..24a448b5f9 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -21,6 +21,7 @@ */ #include +#include #include @@ -342,42 +343,49 @@ static float get_delay(void) { } /** A callback function that is called when the - * pa_context_get_sink_input_info() operation completes. Saves the - * volume field of the specified structure to the global variable volume. */ + * pa_context_get_sink_input_info() operation completes. */ static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, int is_last, void *userdata) { - struct pa_cvolume *volume = userdata; + struct pa_sink_input_info *pi = userdata; if (is_last < 0) { GENERIC_ERR_MSG(context, "Failed to get sink input info"); return; } if (!i) return; - *volume = i->volume; + *pi = *i; pa_threaded_mainloop_signal(mainloop, 0); } static int control(int cmd, void *arg) { switch (cmd) { + case AOCONTROL_GET_MUTE: // fallthrough case AOCONTROL_GET_VOLUME: { + struct pa_sink_input_info pi; ao_control_vol_t *vol = arg; uint32_t devidx = pa_stream_get_index(stream); - struct pa_cvolume volume; pa_threaded_mainloop_lock(mainloop); - if (!waitop(pa_context_get_sink_input_info(context, devidx, info_func, &volume))) { + if (!waitop(pa_context_get_sink_input_info(context, devidx, info_func, &pi))) { GENERIC_ERR_MSG(context, "pa_stream_get_sink_input_info() failed"); return CONTROL_ERROR; } - - if (volume.channels != 2) - vol->left = vol->right = pa_cvolume_avg(&volume)*100/PA_VOLUME_NORM; - else { - vol->left = volume.values[0]*100/PA_VOLUME_NORM; - vol->right = volume.values[1]*100/PA_VOLUME_NORM; + // Warning: some information in pi might be unaccessible, because + // we naively copied the struct, without updating pointers etc. + // Pointers might point to invalid data, accessors might fail. + if (cmd == AOCONTROL_GET_VOLUME) { + if (pi.volume.channels != 2) + vol->left = vol->right = pa_cvolume_avg(&pi.volume)*100/PA_VOLUME_NORM; + else { + vol->left = pi.volume.values[0]*100/PA_VOLUME_NORM; + vol->right = pi.volume.values[1]*100/PA_VOLUME_NORM; + } + } else if (cmd == AOCONTROL_GET_MUTE) { + vol->left = vol->right = pi.mute ? 0.0f : 1.0f; } return CONTROL_OK; } + case AOCONTROL_SET_MUTE: // fallthrough case AOCONTROL_SET_VOLUME: { const ao_control_vol_t *vol = arg; pa_operation *o; @@ -392,12 +400,23 @@ static int control(int cmd, void *arg) { } pa_threaded_mainloop_lock(mainloop); - o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL); - if (!o) { - pa_threaded_mainloop_unlock(mainloop); - GENERIC_ERR_MSG(context, "pa_context_set_sink_input_volume() failed"); - return CONTROL_ERROR; - } + if (cmd == AOCONTROL_SET_VOLUME) { + o = pa_context_set_sink_input_volume(context, pa_stream_get_index(stream), &volume, NULL, NULL); + if (!o) { + pa_threaded_mainloop_unlock(mainloop); + GENERIC_ERR_MSG(context, "pa_context_set_sink_input_volume() failed"); + return CONTROL_ERROR; + } + } else if (cmd == AOCONTROL_SET_MUTE) { + int mute = vol->left == 0.0f || vol->right == 0.0f; + o = pa_context_set_sink_input_mute(context, pa_stream_get_index(stream), mute, NULL, NULL); + if (!o) { + pa_threaded_mainloop_unlock(mainloop); + GENERIC_ERR_MSG(context, "pa_context_set_sink_input_mute() failed"); + return CONTROL_ERROR; + } + } else + abort(); /* We don't wait for completion here */ pa_operation_unref(o); pa_threaded_mainloop_unlock(mainloop); -- cgit v1.2.3 From f7c2ecebccc4b3c5d6299aee5b8f4d382fa78987 Mon Sep 17 00:00:00 2001 From: wm4 Date: Tue, 17 Jan 2012 07:58:57 +0100 Subject: ao_coreaudio: fix partial volume control If digital pass-through is used, this supported setting the volume (just mute, actually), but not getting the volume. This will probably lead to a stuck mute state in the mplayer frontend. Make the code respond to volume queries even if digital pass-through is used. Ideally, ao_coreaudio should implement full mute control, but I can't even test on OSX. --- libao2/ao_coreaudio.c | 6 +++++- 1 fi