From 38b2c97fd6b59b923ad309c19c457cbc0210da17 Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 19 Sep 2013 14:32:09 +0200 Subject: mixer: refactor, fix some aspects of --volume handling Refactor how mixer.c does volume/mute restoration and initialization. Move to handling of --volume and --mute to mixer.c. Simplify the implementation of these and hopefully fix bugs/strange behavior related to using them as file-local options (this uses a somewhat dirty trick: the option values are reverted to "auto" after initialization). Put most code related to initialization and volume restoring in probe_softvol() and restore_volume(). Having this code all in one place is less confusing. Instead of trying to detect whether to use softvol at runtime, detect it at initialization time using AOCONTROL_GET_VOLUME (same with mute, AOCONTROL_GET_MUTE). This implies we expect SET_VOLUME/SET_MUTE to work if the GET variants work. Hopefully this is always the case. This is also preparation for being able to change volume/mute settings if audio is disabled, and for allowing restoring value with playback resume. --- audio/mixer.c | 176 +++++++++++++++++++++++++++++++++++++--------------------- audio/mixer.h | 16 ++++-- 2 files changed, 124 insertions(+), 68 deletions(-) (limited to 'audio') diff --git a/audio/mixer.c b/audio/mixer.c index 97e9417fc1..38c93032a1 100644 --- a/audio/mixer.c +++ b/audio/mixer.c @@ -26,34 +26,34 @@ #include "mpvcore/mp_msg.h" #include "mixer.h" +void mixer_init(struct mixer *mixer, struct MPOpts *opts) +{ + mixer->opts = opts; + mixer->vol_l = mixer->vol_r = 100; + mixer->driver = ""; +} static void checkvolume(struct mixer *mixer) { if (!mixer->ao) return; - if (mixer->softvol == SOFTVOL_AUTO) { - mixer->softvol = mixer->ao->per_application_mixer - || mixer->ao->no_persistent_volume - ? SOFTVOL_NO : SOFTVOL_YES; - } - - ao_control_vol_t vol; - if (mixer->softvol || CONTROL_OK != ao_control(mixer->ao, - AOCONTROL_GET_VOLUME, &vol)) { - mixer->softvol = SOFTVOL_YES; - if (!mixer->af) - return; + ao_control_vol_t vol = {mixer->vol_l, mixer->vol_r}; + if (mixer->softvol) { float vals[AF_NCH]; if (!af_control_any_rev(mixer->af, AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET, vals)) vals[0] = vals[1] = 1.0; vol.left = (vals[0] / (mixer->opts->softvol_max / 100.0)) * 100.0; vol.right = (vals[1] / (mixer->opts->softvol_max / 100.0)) * 100.0; + } else { + // Rely on the values not changing if the query is not supported + ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol); + ao_control(mixer->ao, AOCONTROL_GET_MUTE, &mixer->muted); } float l = mixer->vol_l; float r = mixer->vol_r; - if (mixer->muted_using_volume) + if (mixer->emulate_mute && mixer->muted) l = r = 0; /* Try to detect cases where the volume has been changed by some external * action (such as something else changing a shared system-wide volume). @@ -65,14 +65,10 @@ static void checkvolume(struct mixer *mixer) if (FFABS(vol.left - l) >= 3 || FFABS(vol.right - r) >= 3) { mixer->vol_l = vol.left; mixer->vol_r = vol.right; - if (mixer->muted_using_volume) + if (mixer->emulate_mute) mixer->muted = false; } - if (!mixer->softvol) - // Rely on the value not changing if the query is not supported - ao_control(mixer->ao, AOCONTROL_GET_MUTE, &mixer->muted); mixer->muted_by_us &= mixer->muted; - mixer->muted_using_volume &= mixer->muted; } void mixer_getvolume(mixer_t *mixer, float *l, float *r) @@ -86,17 +82,11 @@ static void setvolume_internal(mixer_t *mixer, float l, float r) { struct ao_control_vol vol = {.left = l, .right = r}; if (!mixer->softvol) { - // relies on the driver data being permanent (so ptr stays valid) - mixer->restore_volume = mixer->ao->no_persistent_volume ? - mixer->ao->driver->info->short_name : NULL; if (ao_control(mixer->ao, AOCONTROL_SET_VOLUME, &vol) != CONTROL_OK) mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] Failed to change audio output volume.\n"); return; } - mixer->restore_volume = "softvol"; - if (!mixer->af) - return; float vals[AF_NCH]; vals[0] = l / 100.0 * mixer->opts->softvol_max / 100.0; vals[1] = r / 100.0 * mixer->opts->softvol_max / 100.0; @@ -106,8 +96,7 @@ static void setvolume_internal(mixer_t *mixer, float l, float r) AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vals)) { - mp_tmsg(MSGT_GLOBAL, mixer->softvol ? MSGL_V : MSGL_WARN, - "[Mixer] No hardware mixing, inserting volume filter.\n"); + mp_tmsg(MSGT_GLOBAL, MSGL_V, "[Mixer] Inserting volume filter.\n"); if (!(af_add(mixer->af, "volume", NULL) && af_control_any_rev(mixer->af, AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, @@ -119,13 +108,13 @@ static void setvolume_internal(mixer_t *mixer, float l, float r) void mixer_setvolume(mixer_t *mixer, float l, float r) { - checkvolume(mixer); // to check mute status and AO support for volume + checkvolume(mixer); // to check mute status + if (mixer->vol_l == l && mixer->vol_r == r) + return; // just prevent af_volume insertion when not needed mixer->vol_l = av_clipf(l, 0, 100); mixer->vol_r = av_clipf(r, 0, 100); - if (!mixer->ao || mixer->muted_using_volume) - return; - setvolume_internal(mixer, mixer->vol_l, mixer->vol_r); - mixer->user_set_volume = true; + if (mixer->ao && !(mixer->emulate_mute && mixer->muted)) + setvolume_internal(mixer, mixer->vol_l, mixer->vol_r); } void mixer_getbothvolume(mixer_t *mixer, float *b) @@ -138,17 +127,20 @@ void mixer_getbothvolume(mixer_t *mixer, float *b) void mixer_setmute(struct mixer *mixer, bool mute) { checkvolume(mixer); - if (mute != mixer->muted) { - if (!mixer->softvol && !mixer->muted_using_volume && ao_control( - mixer->ao, AOCONTROL_SET_MUTE, &mute) == CONTROL_OK) { - mixer->muted_using_volume = false; - } else { + if (mute == mixer->muted) + return; + if (mixer->ao) { + mixer->muted = mute; + mixer->muted_by_us = mute; + if (mixer->emulate_mute) { setvolume_internal(mixer, mixer->vol_l*!mute, mixer->vol_r*!mute); - mixer->muted_using_volume = mute; + } else { + ao_control(mixer->ao, AOCONTROL_SET_MUTE, &mute); } + checkvolume(mixer); + } else { mixer->muted = mute; mixer->muted_by_us = mute; - mixer->user_set_mute = true; } } @@ -160,8 +152,9 @@ bool mixer_getmute(struct mixer *mixer) static void addvolume(struct mixer *mixer, float d) { - checkvolume(mixer); - mixer_setvolume(mixer, mixer->vol_l + d, mixer->vol_r + d); + float vol_l, vol_r; + mixer_getvolume(mixer, &vol_l, &vol_r); + mixer_setvolume(mixer, vol_l + d, vol_r + d); if (d > 0) mixer_setmute(mixer, false); } @@ -237,37 +230,95 @@ void mixer_setbalance(mixer_t *mixer, float val) AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); } +static void probe_softvol(struct mixer *mixer) +{ + if (mixer->opts->softvol == SOFTVOL_AUTO) { + // No system-wide volume => fine with AO volume control. + mixer->softvol = !(mixer->ao->per_application_mixer || + mixer->ao->no_persistent_volume); + } else { + mixer->softvol = mixer->opts->softvol == SOFTVOL_YES; + } + + // If we can't use real volume control => force softvol. + if (!mixer->softvol) { + ao_control_vol_t vol; + if (ao_control(mixer->ao, AOCONTROL_GET_VOLUME, &vol) != CONTROL_OK) { + mixer->softvol = true; + mp_tmsg(MSGT_GLOBAL, MSGL_WARN, + "[mixer] Hardware volume control unavailable.\n"); + } + } + + // Probe native mute support. + mixer->emulate_mute = true; + if (!mixer->softvol) { + if (ao_control(mixer->ao, AOCONTROL_GET_MUTE, &(bool){0}) == CONTROL_OK) + mixer->emulate_mute = false; + } +} + +static void restore_volume(struct mixer *mixer) +{ + struct MPOpts *opts = mixer->opts; + struct ao *ao = mixer->ao; + + float force_vol_l = -1, force_vol_r = -1; + int force_mute = -1; + + const char *prev_driver = mixer->driver; + mixer->driver = mixer->softvol ? "softvol" : ao->driver->info->short_name; + + bool restore = mixer->softvol || ao->no_persistent_volume; + + // Restore old parameters if volume won't survive reinitialization. + // But not if volume scale is possibly different. + if (restore && strcmp(mixer->driver, prev_driver) == 0) { + force_vol_l = mixer->vol_l; + force_vol_r = mixer->vol_r; + } + + // Set mute if we disabled it on uninit last time. + if (mixer->muted_by_us) + force_mute = 1; + + // Set parameters from command line. + if (opts->mixer_init_volume >= 0) + force_vol_l = force_vol_r = opts->mixer_init_volume; + if (opts->mixer_init_mute >= 0) + force_mute = opts->mixer_init_mute; + + // Using --volume should not reset the volume on every file (i.e. reinit), + // OTOH mpv --{ --volume 10 f1.mkv --} --{ --volume 20 f2.mkv --} must work. + // Resetting the option volumes to "auto" (-1) is easiest. If file local + // options (as shown above) are used, these are reset to other values. + opts->mixer_init_volume = -1; + opts->mixer_init_mute = -1; + + checkvolume(mixer); + if (force_vol_l >= 0 && force_vol_r >= 0) + mixer_setvolume(mixer, force_vol_l, force_vol_r); + if (force_mute >= 0) + mixer_setmute(mixer, force_mute); +} + // Called after the audio filter chain is built or rebuilt. +// (Can be called multiple times, even without mixer_uninit() in-between.) void mixer_reinit_audio(struct mixer *mixer, struct ao *ao, struct af_stream *af) { + if (!ao || !af) + return; mixer->ao = ao; mixer->af = af; - mixer->softvol = mixer->opts->softvol; - /* Use checkvolume() to see if softvol needs to be enabled because of - * lacking AO support, but first store values it could overwrite. */ - float left = mixer->vol_l, right = mixer->vol_r; - bool muted = mixer->muted_by_us; - checkvolume(mixer); - /* Try to avoid restoring volume stored from one control method with - * another. Especially, restoring softvol volume (typically high) on - * system mixer could have very nasty effects. */ - const char *restore_reason = mixer->softvol ? "softvol" : - mixer->ao->driver->info->short_name; - if (mixer->restore_volume && !strcmp(mixer->restore_volume, - restore_reason)) - mixer_setvolume(mixer, left, right); - /* We turn mute off at AO uninit, so it has to be restored (unless - * we're reinitializing filter chain while keeping AO); but we only - * enable mute, not turn external mute off. */ - if (muted) - mixer_setmute(mixer, true); + + probe_softvol(mixer); + restore_volume(mixer); + if (mixer->balance != 0) mixer_setbalance(mixer, mixer->balance); - mixer->user_set_mute = false; - mixer->user_set_volume = false; } -/* Called before uninitializing the audio output. The main purpose is to +/* Called before uninitializing the audio filter chain. The main purpose is to * turn off mute, in case it's a global/persistent setting which might * otherwise be left enabled even after this player instance exits. */ @@ -291,4 +342,5 @@ void mixer_uninit_audio(struct mixer *mixer) mixer->muted_by_us = true; } mixer->ao = NULL; + mixer->af = NULL; } diff --git a/audio/mixer.h b/audio/mixer.h index b22bba0310..6761be5da4 100644 --- a/audio/mixer.h +++ b/audio/mixer.h @@ -21,6 +21,7 @@ #include +// Values of MPOpts.softvol enum { SOFTVOL_NO = 0, SOFTVOL_YES = 1, @@ -31,19 +32,22 @@ typedef struct mixer { struct MPOpts *opts; struct ao *ao; struct af_stream *af; - int softvol; + // Static, dependent on ao/softvol settings + bool softvol; // use AO (true) or af_volume (false) + bool emulate_mute; // if true, emulate mute with volume=0 + // Last known values (possibly out of sync with reality) + float vol_l, vol_r; bool muted; + // Used to decide whether we should unmute on uninit bool muted_by_us; - bool muted_using_volume; - float vol_l, vol_r; /* Contains ao driver name or "softvol" if volume is not persistent * and needs to be restored after the driver is reinitialized. */ - const char *restore_volume; + const char *driver; + // Other stuff float balance; - bool user_set_mute; - bool user_set_volume; } mixer_t; +void mixer_init(struct mixer *mixer, struct MPOpts *opts); void mixer_reinit_audio(struct mixer *mixer, struct ao *ao, struct af_stream *af); void mixer_uninit_audio(struct mixer *mixer); void mixer_getvolume(mixer_t *mixer, float *l, float *r); -- cgit v1.2.3