diff options
-rw-r--r-- | DOCS/man/en/af.rst | 2 | ||||
-rw-r--r-- | DOCS/man/en/changes.rst | 1 | ||||
-rw-r--r-- | DOCS/man/en/options.rst | 12 | ||||
-rw-r--r-- | audio/filter/af.c | 41 | ||||
-rw-r--r-- | audio/filter/af_volume.c | 13 | ||||
-rw-r--r-- | audio/mixer.c | 302 | ||||
-rw-r--r-- | audio/mixer.h | 46 | ||||
-rw-r--r-- | audio/out/ao_alsa.c | 1 | ||||
-rw-r--r-- | audio/out/ao_oss.c | 1 | ||||
-rw-r--r-- | mpvcore/command.c | 42 | ||||
-rw-r--r-- | mpvcore/mp_core.h | 4 | ||||
-rw-r--r-- | mpvcore/mplayer.c | 50 | ||||
-rw-r--r-- | mpvcore/options.c | 1 | ||||
-rw-r--r-- | mpvcore/options.h | 1 |
14 files changed, 290 insertions, 227 deletions
diff --git a/DOCS/man/en/af.rst b/DOCS/man/en/af.rst index a6ad32989c..8844077781 100644 --- a/DOCS/man/en/af.rst +++ b/DOCS/man/en/af.rst @@ -438,7 +438,7 @@ Available filters are: ``drc[=method:target]`` Applies dynamic range compression. This maximizes the volume by compressing - the audio signal's dynamic range. + the audio signal's dynamic range. (Formerly called ``volnorm``.) ``<method>`` Sets the used method. diff --git a/DOCS/man/en/changes.rst b/DOCS/man/en/changes.rst index 8996a90336..489fadd36c 100644 --- a/DOCS/man/en/changes.rst +++ b/DOCS/man/en/changes.rst @@ -172,6 +172,7 @@ Command Line Switches ``--mixer`` AO suboptions (``alsa``, ``oss``) ``--mixer-channel`` AO suboptions (``alsa``, ``oss``) ``--ao=alsa:device=hw=0.3`` ``--ao=alsa:device=[hw:0,3]`` + ``-af volnorm`` ``--af=drc`` (renamed) =========================== ======================================== .. note:: diff --git a/DOCS/man/en/options.rst b/DOCS/man/en/options.rst index 1c07403ea7..766118e4c6 100644 --- a/DOCS/man/en/options.rst +++ b/DOCS/man/en/options.rst @@ -2589,6 +2589,18 @@ Set the startup volume. A value of -1 (the default) will not change the volume. See also ``--softvol``. +``--volume-restore-data=<string>`` + Used internally for use by playback resume (e.g. with ``quit_watch_later``). + Restoring value has to be done carefully, because different AOs as well as + softvol can have different value ranges, and we don't want to restore + volume if setting the volume changes it system wide. The normal options + (like ``--volume``) would always set the volume. This option was added for + restoring volume in a safer way (by storing the method used to set the + volume), and is not generally useful. Its semantics are considered private + to mpv. + + Do not use. + ``--wid=<ID>`` (X11 and Windows only) This tells mpv to attach to an existing window. The ID is interpreted as diff --git a/audio/filter/af.c b/audio/filter/af.c index 7a8dc406cf..00eb2346f9 100644 --- a/audio/filter/af.c +++ b/audio/filter/af.c @@ -437,8 +437,6 @@ static int af_fix_rate(struct af_stream *s, struct af_instance **p_af, // state (for example, format filters that were tentatively inserted stay // inserted). // In that case, you should always rebuild the filter chain, or abort. -// Also, note that for complete reinit, fixup_output_format() may have to be -// called after this function. static int af_reinit(struct af_stream *s) { // Start with the second filter, as the first filter is the special input @@ -496,7 +494,12 @@ static int af_reinit(struct af_stream *s) af_print_filter_chain(s, NULL, MSGL_V); - return AF_OK; + /* Set previously unset fields in s->output to those of the filter chain + * output. This is used to make the output format fixed, and even if you + * insert new filters or change the input format, the output format won't + * change. (Audio outputs generally can't change format at runtime.) */ + af_copy_unset_fields(&s->output, &s->filter_output); + return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR; negotiate_error: mp_msg(MSGT_AFILTER, MSGL_ERR, "[libaf] Unable to correct audio format. " @@ -547,21 +550,6 @@ void af_destroy(struct af_stream *s) talloc_free(s); } -/* - * Set previously unset fields in s->output to those of the filter chain - * output. This is used to make the output format fixed, and even if you insert - * new filters or change the input format, the output format won't change. - * \return AF_ERROR on error, AF_OK if successful. - */ -static int fixup_output_format(struct af_stream *s) -{ - if (AF_OK != af_reinit(s)) - return AF_ERROR; - - af_copy_unset_fields(&s->output, &s->filter_output); - return af_config_equals(&s->output, &s->filter_output) ? AF_OK : AF_ERROR; -} - /* Initialize the stream "s". This function creates a new filter list if necessary according to the values set in input and output. Input and output should contain the format of the current movie and the @@ -586,23 +574,18 @@ int af_init(struct af_stream *s) // Add all filters in the list (if there are any) struct m_obj_settings *list = s->opts->af_settings; for (int i = 0; list && list[i].name; i++) { - if (!af_prepend(s, s->last, list[i].name, list[i].attribs)) + if (!af_prepend(s, s->last, list[i].name, list[i].attribs)) { + af_uninit(s); return -1; + } } } remove_auto_inserted_filters(s); - // Init filters - if (AF_OK != af_reinit(s)) - return -1; - - if (AF_OK != fixup_output_format(s)) { + if (af_reinit(s) != AF_OK) { // Something is stuffed audio out will not work - mp_msg( - MSGT_AFILTER, MSGL_ERR, - "[libaf] Unable to setup filter system can not" - " meet sound-card demands, please send a bug report. \n"); + mp_msg(MSGT_AFILTER, MSGL_ERR, "Could not create audio filter chain.\n"); af_uninit(s); return -1; } @@ -628,7 +611,7 @@ struct af_instance *af_add(struct af_stream *s, char *name, char **args) return NULL; // Reinitalize the filter list - if (af_reinit(s) != AF_OK || fixup_output_format(s) != AF_OK) { + if (af_reinit(s) != AF_OK) { af_uninit(s); af_init(s); return NULL; diff --git a/audio/filter/af_volume.c b/audio/filter/af_volume.c index 82c31eaa12..a766b97ec6 100644 --- a/audio/filter/af_volume.c +++ b/audio/filter/af_volume.c @@ -81,17 +81,22 @@ static int control(struct af_instance* af, int cmd, void* arg) } return af_test_output(af,(struct mp_audio*)arg); case AF_CONTROL_COMMAND_LINE:{ - float v=0.0; + float v=1000.0; float vol[AF_NCH]; int i; sscanf((char*)arg,"%f:%i:%i", &v, &s->soft, &s->fast); - for(i=0;i<AF_NCH;i++) vol[i]=v; + float dest = 0.0; + if (v < 1000) + af_from_dB(1,&v,&dest,20.0,-200.0,60.0); + for(i=0;i<AF_NCH;i++) vol[i]=dest; return control(af,AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, vol); } case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET: - return af_from_dB(AF_NCH,(float*)arg,s->level,20.0,-200.0,60.0); + memcpy(s->level, arg, sizeof(float) * AF_NCH); + return AF_OK; case AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_GET: - return af_to_dB(AF_NCH,s->level,(float*)arg,20.0); + memcpy(arg, s->level, sizeof(float) * AF_NCH); + return AF_OK; case AF_CONTROL_PRE_DESTROY:{ float m = 0.0; int i; diff --git a/audio/mixer.c b/audio/mixer.c index d3762628ae..07ce229f73 100644 --- a/audio/mixer.c +++ b/audio/mixer.c @@ -17,6 +17,7 @@ */ #include <string.h> +#include <stdio.h> #include <libavutil/common.h> @@ -24,38 +25,61 @@ #include "audio/out/ao.h" #include "audio/filter/af.h" #include "mpvcore/mp_msg.h" +#include "talloc.h" #include "mixer.h" +struct mixer { + struct MPOpts *opts; + struct ao *ao; + struct af_stream *af; + // 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; + /* Contains ao driver name or "softvol" if volume is not persistent + * and needs to be restored after the driver is reinitialized. */ + const char *driver; + // Other stuff + float balance; +}; + +struct mixer *mixer_init(void *talloc_ctx, struct MPOpts *opts) +{ + struct mixer *mixer = talloc_ptrtype(talloc_ctx, mixer); + *mixer = (struct mixer) { + .opts = opts, + .vol_l = 100, + .vol_r = 100, + .driver = "", + }; + return mixer; +} 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->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] / (mixer->softvol_max / 100.0)) * 100.0; - vol.right = (db_vals[1] / (mixer->softvol_max / 100.0)) * 100.0; + 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). @@ -67,73 +91,59 @@ 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) +void mixer_getvolume(struct mixer *mixer, float *l, float *r) { checkvolume(mixer); *l = mixer->vol_l; *r = mixer->vol_r; } -static void setvolume_internal(mixer_t *mixer, float l, float r) +static void setvolume_internal(struct mixer *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->afilter) - return; - // af_volume uses values in dB - float db_vals[AF_NCH]; - int i; - db_vals[0] = (l / 100.0) * (mixer->softvol_max / 100.0); - db_vals[1] = (r / 100.0) * (mixer->softvol_max / 100.0); - for (i = 2; i < AF_NCH; i++) - db_vals[i] = ((l + r) / 100.0) * (mixer->softvol_max / 100.0) / 2.0; - af_to_dB(AF_NCH, db_vals, db_vals, 20.0); - if (!af_control_any_rev(mixer->afilter, + 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; + for (int i = 2; i < AF_NCH; i++) + vals[i] = (vals[0] + vals[1]) / 2.0; + if (!af_control_any_rev(mixer->af, AF_CONTROL_VOLUME_LEVEL | AF_CONTROL_SET, - db_vals)) + vals)) { - mp_tmsg(MSGT_GLOBAL, mixer->softvol ? MSGL_V : MSGL_WARN, - "[Mixer] No hardware mixing, inserting volume filter.\n"); - if (!(af_add(mixer->afilter, "volume", NULL) - && af_control_any_rev(mixer->afilter, + 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, - db_vals))) + vals))) mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No volume control available.\n"); } } -void mixer_setvolume(mixer_t *mixer, float l, float r) +void mixer_setvolume(struct mixer *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) +void mixer_getbothvolume(struct mixer *mixer, float *b) { float mixer_l, mixer_r; mixer_getvolume(mixer, &mixer_l, &mixer_r); @@ -143,17 +153,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; } } @@ -165,26 +178,25 @@ 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); - if (d > 0) - mixer_setmute(mixer, false); + float vol_l, vol_r; + mixer_getvolume(mixer, &vol_l, &vol_r); + mixer_setvolume(mixer, vol_l + d, vol_r + d); } -void mixer_incvolume(mixer_t *mixer) +void mixer_incvolume(struct mixer *mixer) { - addvolume(mixer, mixer->volstep); + addvolume(mixer, mixer->opts->volstep); } -void mixer_decvolume(mixer_t *mixer) +void mixer_decvolume(struct mixer *mixer) { - addvolume(mixer, -mixer->volstep); + addvolume(mixer, -mixer->opts->volstep); } -void mixer_getbalance(mixer_t *mixer, float *val) +void mixer_getbalance(struct mixer *mixer, float *val) { - if (mixer->afilter) - af_control_any_rev(mixer->afilter, + if (mixer->af) + af_control_any_rev(mixer->af, AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET, &mixer->balance); *val = mixer->balance; @@ -201,7 +213,7 @@ void mixer_getbalance(mixer_t *mixer, float *val) * are significantly below 1 will be overwritten with much higher values. */ -void mixer_setbalance(mixer_t *mixer, float val) +void mixer_setbalance(struct mixer *mixer, float val) { float level[AF_NCH]; int i; @@ -210,23 +222,22 @@ void mixer_setbalance(mixer_t *mixer, float val) mixer->balance = val; - if (!mixer->afilter) + if (!mixer->af) return; - if (af_control_any_rev(mixer->afilter, + if (af_control_any_rev(mixer->af, AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) return; if (val == 0 || mixer->ao->channels.num < 2) return; - if (!(af_pan_balance = af_add(mixer->afilter, "pan", NULL))) { + if (!(af_pan_balance = af_add(mixer->af, "pan", NULL))) { 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++) { @@ -242,39 +253,125 @@ void mixer_setbalance(mixer_t *mixer, float val) AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); } +char *mixer_get_volume_restore_data(struct mixer *mixer) +{ + if (!mixer->driver[0]) + return NULL; + return talloc_asprintf(NULL, "%s:%f:%f:%d", mixer->driver, mixer->vol_l, + mixer->vol_r, mixer->muted_by_us); +} + +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; + + // Set parameters from playback resume. + char *restore_data = mixer->opts->mixer_restore_volume_data; + if (restore && restore_data && restore_data[0]) { + char drv[40]; + float v_l, v_r; + int m; + if (sscanf(restore_data, "%39[^:]:%f:%f:%d", drv, &v_l, &v_r, &m) == 4) { + if (strcmp(mixer->driver, drv) == 0) { + force_vol_l = v_l; + force_vol_r = v_r; + force_mute = !!m; + } + } + talloc_free(restore_data); + mixer->opts->mixer_restore_volume_data = NULL; + } + + // 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, the option handler code will reset + // them to other values, and force the volume to be reset as well. + 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. -void mixer_reinit(struct mixer *mixer, struct ao *ao) +// (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; - /* 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); + mixer->af = af; + + 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. */ -void mixer_uninit(struct mixer *mixer) +void mixer_uninit_audio(struct mixer *mixer) { if (!mixer->ao) return; @@ -294,4 +391,5 @@ void mixer_uninit(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 3160c20cfe..9fbb4bcdca 100644 --- a/audio/mixer.h +++ b/audio/mixer.h @@ -21,40 +21,30 @@ #include <stdbool.h> +// Values of MPOpts.softvol enum { SOFTVOL_NO = 0, SOFTVOL_YES = 1, SOFTVOL_AUTO = 2, }; -typedef struct mixer { - struct ao *ao; - struct af_stream *afilter; - int volstep; - int softvol; - float softvol_max; - bool muted; - 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; - float balance; - bool user_set_mute; - bool user_set_volume; -} mixer_t; +struct MPOpts; +struct ao; +struct af_stream; +struct mixer; -void mixer_reinit(struct mixer *mixer, struct ao *ao); -void mixer_uninit(struct mixer *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_setmute(mixer_t *mixer, bool mute); -bool mixer_getmute(mixer_t *mixer); -void mixer_getbalance(mixer_t *mixer, float *bal); -void mixer_setbalance(mixer_t *mixer, float bal); +struct mixer *mixer_init(void *talloc_ctx, 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(struct mixer *mixer, float *l, float *r); +void mixer_setvolume(struct mixer *mixer, float l, float r); +void mixer_incvolume(struct mixer *mixer); +void mixer_decvolume(struct mixer *mixer); +void mixer_getbothvolume(struct mixer *mixer, float *b); +void mixer_setmute(struct mixer *mixer, bool mute); +bool mixer_getmute(struct mixer *mixer); +void mixer_getbalance(struct mixer *mixer, float *bal); +void mixer_setbalance(struct mixer *mixer, float bal); +char *mixer_get_volume_restore_data(struct mixer *mixer); #endif /* MPLAYER_MIXER_H */ diff --git a/audio/out/ao_alsa.c b/audio/out/ao_alsa.c index 2db5041b95..ff931e2a0b 100644 --- a/audio/out/ao_alsa.c +++ b/audio/out/ao_alsa.c @@ -38,7 +38,6 @@ #include "config.h" #include "mpvcore/options.h" #include "mpvcore/m_option.h" -#include "audio/mixer.h" #include "mpvcore/mp_msg.h" #define ALSA_PCM_NEW_HW_PARAMS_API diff --git a/audio/out/ao_oss.c b/audio/out/ao_oss.c index 79b96c7bc1..0890f11e0a 100644 --- a/audio/out/ao_oss.c +++ b/audio/out/ao_oss.c @@ -36,7 +36,6 @@ #include "config.h" #include "mpvcore/options.h" #include "mpvcore/mp_msg.h" -#include "audio/mixer.h" #ifdef HAVE_SYS_SOUNDCARD_H #include <sys/soundcard.h> diff --git a/mpvcore/command.c b/mpvcore/command.c index dedac9dc10..a2de363b3d 100644 --- a/mpvcore/command.c +++ b/mpvcore/command.c @@ -762,23 +762,19 @@ static int mp_property_clock(m_option_t *prop, int action, void *arg, static int mp_property_volume(m_option_t *prop, int action, void *arg, MPContext *mpctx) { - - if (!mpctx->sh_audio) - return M_PROPERTY_UNAVAILABLE; - switch (action) { case M_PROPERTY_GET: - mixer_getbothvolume(&mpctx->mixer, arg); + mixer_getbothvolume(mpctx->mixer, arg); return M_PROPERTY_OK; case M_PROPERTY_SET: - mixer_setvolume(&mpctx->mixer, *(float *) arg, *(float *) arg); + mixer_setvolume(mpctx->mixer, *(float *) arg, *(float *) arg); return M_PROPERTY_OK; case M_PROPERTY_SWITCH: { struct m_property_switch_arg *sarg = arg; if (sarg->inc <= 0) - mixer_decvolume(&mpctx->mixer); + mixer_decvolume(mpctx->mixer); else - mixer_incvolume(&mpctx->mixer); + mixer_incvolume(mpctx->mixer); return M_PROPERTY_OK; } } @@ -789,21 +785,32 @@ static int mp_property_volume(m_option_t *prop, int action, void *arg, static int mp_property_mute(m_option_t *prop, int action, void *arg, MPContext *mpctx) { - - if (!mpctx->sh_audio) - return M_PROPERTY_UNAVAILABLE; - switch (action) { case M_PROPERTY_SET: - mixer_setmute(&mpctx->mixer, *(int *) arg); + mixer_setmute(mpctx->mixer, *(int *) arg); return M_PROPERTY_OK; case M_PROPERTY_GET: - *(int *)arg = mixer_getmute(&mpctx->mixer); + *(int *)arg = mixer_getmute(mpctx->mixer); return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; } +static int mp_property_volrestore(m_option_t *prop, int action, + void *arg, MPContext *mpctx) +{ + switch (action) { + case M_PROPERTY_GET: { + char *s = mixer_get_volume_restore_data(mpctx->mixer); + *(char **)arg = s; + return s ? M_PROPERTY_OK : M_PROPERTY_UNAVAILABLE; + } + case M_PROPERTY_SET: + return M_PROPERTY_NOT_IMPLEMENTED; + } + return mp_property_generic_option(prop, action, arg, mpctx); +} + /// Audio delay (RW) static int mp_property_audio_delay(m_option_t *prop, int action, void *arg, MPContext *mpctx) @@ -899,11 +906,11 @@ static int mp_property_balance(m_option_t *prop, int action, void *arg, switch (action) { case M_PROPERTY_GET: - mixer_getbalance(&mpctx->mixer, arg); + mixer_getbalance(mpctx->mixer, arg); return M_PROPERTY_OK; case M_PROPERTY_PRINT: { char **str = arg; - mixer_getbalance(&mpctx->mixer, &bal); + mixer_getbalance(mpctx->mixer, &bal); if (bal == 0.f) *str = talloc_strdup(NULL, "center"); else if (bal == -1.f) @@ -918,7 +925,7 @@ static int mp_property_balance(m_option_t *prop, int action, void *arg, return M_PROPERTY_OK; } case M_PROPERTY_SET: - mixer_setbalance(&mpctx->mixer, *(float *)arg); + mixer_setbalance(mpctx->mixer, *(float *)arg); return M_PROPERTY_OK; } return M_PROPERTY_NOT_IMPLEMENTED; @@ -1806,6 +1813,7 @@ static const m_option_t mp_properties[] = { M_OPTION_PROPERTY_CUSTOM("aid", mp_property_audio), { "balance", mp_property_balance, CONF_TYPE_FLOAT, M_OPT_RANGE, -1, 1, NULL }, + M_OPTION_PROPERTY_CUSTOM("volume-restore-data", mp_property_volrestore), // Video M_OPTION_PROPERTY_CUSTOM("fullscreen", mp_property_fullscreen), diff --git a/mpvcore/mp_core.h b/mpvcore/mp_core.h index 9f3e095110..98096aa5fe 100644 --- a/mpvcore/mp_core.h +++ b/mpvcore/mp_core.h @@ -22,14 +22,12 @@ #include <stdbool.h> #include "mpvcore/options.h" -#include "audio/mixer.h" #include "demux/demux.h" // definitions used internally by the core player code #define INITIALIZED_VO 1 #define INITIALIZED_AO 2 -#define INITIALIZED_VOL 4 #define INITIALIZED_GETCH2 8 #define INITIALIZED_PLAYBACK 16 #define INITIALIZED_STREAM 64 @@ -180,7 +178,7 @@ typedef struct MPContext { // demuxer defines metadata), or special purpose demuxers like TV. struct demuxer *master_demuxer; - mixer_t mixer; + struct mixer *mixer; struct ao *ao; struct vo *video_out; diff --git a/mpvcore/mplayer.c b/mpvcore/mplayer.c index e4566c56ff..f4bf6c9316 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); |