summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--audio/mixer.c176
-rw-r--r--audio/mixer.h16
-rw-r--r--mpvcore/mp_core.h1
-rw-r--r--mpvcore/mplayer.c42
4 files changed, 131 insertions, 104 deletions
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 <stdbool.h>
+// 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);
diff --git a/mpvcore/mp_core.h b/mpvcore/mp_core.h
index 9f3e095110..8f57a65edf 100644
--- a/mpvcore/mp_core.h
+++ b/mpvcore/mp_core.h
@@ -29,7 +29,6 @@
#define INITIALIZED_VO 1
#define INITIALIZED_AO 2
-#define INITIALIZED_VOL 4
#define INITIALIZED_GETCH2 8
#define INITIALIZED_PLAYBACK 16
#define INITIALIZED_STREAM 64
diff --git a/mpvcore/mplayer.c b/mpvcore/mplayer.c
index a2c96bb270..fafb88d022 100644
--- a/mpvcore/mplayer.c
+++ b/mpvcore/mplayer.c
@@ -443,18 +443,16 @@ static void uninit_subs(struct demuxer *demuxer)
void uninit_player(struct MPContext *mpctx, unsigned int mask)
{
- struct MPOpts *opts = mpctx->opts;
-
mask &= mpctx->initialized_flags;
mp_msg(MSGT_CPLAYER, MSGL_DBG2, "\n*** uninit(0x%X)\n", mask);
if (mask & INITIALIZED_ACODEC) {
mpctx->initialized_flags &= ~INITIALIZED_ACODEC;
+ mixer_uninit_audio(&mpctx->mixer);
if (mpctx->sh_audio)
uninit_audio(mpctx->sh_audio);
cleanup_demux_stream(mpctx, STREAM_AUDIO);
- mpctx->mixer.af = NULL;
}
if (mask & INITIALIZED_SUB) {
@@ -526,24 +524,8 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask)
getch2_disable();
}
- if (mask & INITIALIZED_VOL) {
- mpctx->initialized_flags &= ~INITIALIZED_VOL;
- if (mpctx->mixer.ao) {
- // Normally the mixer remembers volume, but do it even if the
- // volume is set explicitly with --volume=... (so that the same
- // volume is restored on reinit)
- if (opts->mixer_init_volume >= 0 && mpctx->mixer.user_set_volume)
- mixer_getbothvolume(&mpctx->mixer, &opts->mixer_init_volume);
- if (opts->mixer_init_mute >= 0 && mpctx->mixer.user_set_mute)
- opts->mixer_init_mute = mixer_getmute(&mpctx->mixer);
- }
- }
-
if (mask & INITIALIZED_AO) {
mpctx->initialized_flags &= ~INITIALIZED_AO;
- if (mpctx->mixer.ao)
- mixer_uninit_audio(&mpctx->mixer);
- mpctx->mixer.ao = NULL;
if (mpctx->ao)
ao_uninit(mpctx->ao, mpctx->stop_play != AT_END_OF_FILE);
mpctx->ao = NULL;
@@ -1618,7 +1600,6 @@ static int build_afilter_chain(struct MPContext *mpctx)
static int recreate_audio_filters(struct MPContext *mpctx)
{
- struct MPOpts *opts = mpctx->opts;
assert(mpctx->sh_audio);
// init audio filters:
@@ -1629,15 +1610,6 @@ static int recreate_audio_filters(struct MPContext *mpctx)
}
mixer_reinit_audio(&mpctx->mixer, mpctx->ao, mpctx->sh_audio->afilter);
- if (!(mpctx->initialized_flags & INITIALIZED_VOL)) {
- if (opts->mixer_init_volume >= 0) {
- mixer_setvolume(&mpctx->mixer, opts->mixer_init_volume,
- opts->mixer_init_volume);
- }
- if (opts->mixer_init_mute >= 0)
- mixer_setmute(&mpctx->mixer, opts->mixer_init_mute);
- mpctx->initialized_flags |= INITIALIZED_VOL;
- }
return 0;
}
@@ -1662,7 +1634,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
struct MPOpts *opts = mpctx->opts;
init_demux_stream(mpctx, STREAM_AUDIO);
if (!mpctx->sh_audio) {
- uninit_player(mpctx, INITIALIZED_VOL | INITIALIZED_AO);
+ uninit_player(mpctx, INITIALIZED_AO);
goto no_audio;
}
@@ -1733,7 +1705,7 @@ void reinit_audio_chain(struct MPContext *mpctx)
return;
init_error:
- uninit_player(mpctx, INITIALIZED_ACODEC | INITIALIZED_AO | INITIALIZED_VOL);
+ uninit_player(mpctx, INITIALIZED_ACODEC | INITIALIZED_AO);
cleanup_demux_stream(mpctx, STREAM_AUDIO);
no_audio:
mpctx->current_track[STREAM_AUDIO] = NULL;
@@ -2040,7 +2012,7 @@ void mp_switch_track(struct MPContext *mpctx, enum stream_type type,
uninit_player(mpctx, INITIALIZED_VCODEC |
(mpctx->opts->fixed_vo && track ? 0 : INITIALIZED_VO));
} else if (type == STREAM_AUDIO) {
- uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC | INITIALIZED_VOL);
+ uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_ACODEC);
} else if (type == STREAM_SUB) {
uninit_player(mpctx, INITIALIZED_SUB);
}
@@ -2280,7 +2252,7 @@ static int fill_audio_out_buffers(struct MPContext *mpctx, double endpts)
* while displaying video, then doing the output format switch.
*/
if (!mpctx->opts->gapless_audio)
- uninit_player(mpctx, INITIALIZED_AO | INITIALIZED_VOL);
+ uninit_player(mpctx, INITIALIZED_AO);
reinit_audio_chain(mpctx);
return -1;
} else if (res == ASYNC_PLAY_DONE)
@@ -2909,7 +2881,7 @@ static bool timeline_set_part(struct MPContext *mpctx, int i, bool force)
enum stop_play_reason orig_stop_play = mpctx->stop_play;
if (!mpctx->sh_video && mpctx->stop_play == KEEP_PLAYING)
mpctx->stop_play = AT_END_OF_FILE; // let audio uninit drain data
- uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts->fixed_vo ? 0 : INITIALIZED_VO) | (mpctx->opts->gapless_audio ? 0 : INITIALIZED_AO) | INITIALIZED_VOL | INITIALIZED_ACODEC | INITIALIZED_SUB);
+ uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts->fixed_vo ? 0 : INITIALIZED_VO) | (mpctx->opts->gapless_audio ? 0 : INITIALIZED_AO) | INITIALIZED_ACODEC | INITIALIZED_SUB);
mpctx->stop_play = orig_stop_play;
mpctx->demuxer = n->source;
@@ -4855,7 +4827,7 @@ static int mpv_main(int argc, char *argv[])
init_libav();
GetCpuCaps(&gCpuCaps);
screenshot_init(mpctx);
- mpctx->mixer.opts = opts;
+ mixer_init(&mpctx->mixer, opts);
// Preparse the command line
m_config_preparse_command_line(mpctx->mconfig, argc, argv);