diff options
author | wm4 <wm4@mplayer2.org> | 2012-04-28 00:39:19 +0200 |
---|---|---|
committer | wm4 <wm4@mplayer2.org> | 2012-04-28 00:54:26 +0200 |
commit | 87f4cafe9c0881743d1117c2e8cd7e3376e33302 (patch) | |
tree | f41caa5aad8969d769a777c0a7912751721a66bd | |
parent | 1324eaece08d84fbe3eb539642dba99b74dd1c07 (diff) | |
parent | b711624ef350d1e971f5fcc57eb4af9f74233d2a (diff) | |
download | mpv-87f4cafe9c0881743d1117c2e8cd7e3376e33302.tar.bz2 mpv-87f4cafe9c0881743d1117c2e8cd7e3376e33302.tar.xz |
Merge remote-tracking branch 'origin/master'
Conflicts:
command.c
libao2/ao_alsa.c
libao2/ao_dsound.c
libao2/ao_pulse.c
libao2/audio_out.h
mixer.c
mixer.h
mplayer.c
Replace my mixer changes with uau's implementation, which is based on
my code.
-rw-r--r-- | command.c | 10 | ||||
-rw-r--r-- | libao2/ao_alsa.c | 33 | ||||
-rw-r--r-- | libao2/ao_oss.c | 16 | ||||
-rw-r--r-- | libao2/ao_pulse.c | 47 | ||||
-rw-r--r-- | libao2/ao_sgi.c | 7 | ||||
-rw-r--r-- | libao2/ao_sun.c | 5 | ||||
-rw-r--r-- | libao2/audio_out.c | 4 | ||||
-rw-r--r-- | libao2/audio_out.h | 61 | ||||
-rw-r--r-- | libmpdemux/demux_lavf.c | 18 | ||||
-rw-r--r-- | mixer.c | 374 | ||||
-rw-r--r-- | mixer.h | 22 | ||||
-rw-r--r-- | mplayer.c | 3 | ||||
-rw-r--r-- | stream/stream_pvr.c | 2 | ||||
-rw-r--r-- | sub/ass_mp.c | 7 |
14 files changed, 282 insertions, 327 deletions
@@ -755,16 +755,15 @@ static int mp_property_mute(m_option_t *prop, int action, void *arg, case M_PROPERTY_SET: if (!arg) return M_PROPERTY_ERROR; - mixer_setmuted(&mpctx->mixer, *(int *) arg); + mixer_setmute(&mpctx->mixer, *(int *) arg); return M_PROPERTY_OK; case M_PROPERTY_STEP_UP: case M_PROPERTY_STEP_DOWN: - mixer_mute(&mpctx->mixer); + mixer_setmute(&mpctx->mixer, !mixer_getmute(&mpctx->mixer)); return M_PROPERTY_OK; default: return m_property_flag_ro(prop, action, arg, - mixer_getmuted(&mpctx->mixer)); - + mixer_getmute(&mpctx->mixer)); } } @@ -869,9 +868,6 @@ static int mp_property_balance(m_option_t *prop, int action, void *arg, { float bal; - if (!mpctx->sh_audio || mpctx->sh_audio->channels < 2) - return M_PROPERTY_UNAVAILABLE; - switch (action) { case M_PROPERTY_GET: if (!arg) diff --git a/libao2/ao_alsa.c b/libao2/ao_alsa.c index 65e7fda178..ff837e7d30 100644 --- a/libao2/ao_alsa.c +++ b/libao2/ao_alsa.c @@ -97,15 +97,11 @@ static void alsa_error_handler(const char *file, int line, const char *function, 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: { - ao_control_vol_t *vol = (ao_control_vol_t *)arg; - int err; snd_mixer_t *handle; snd_mixer_elem_t *elem; @@ -189,6 +185,7 @@ static int control(int cmd, void *arg) switch (cmd) { case AOCONTROL_SET_VOLUME: { + ao_control_vol_t *vol = arg; set_vol = vol->left / f_multi + pmin + 0.5; //setting channels @@ -211,6 +208,7 @@ static int control(int cmd, void *arg) break; } case AOCONTROL_GET_VOLUME: { + ao_control_vol_t *vol = arg; 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); @@ -219,28 +217,29 @@ static int control(int cmd, void *arg) break; } case AOCONTROL_SET_MUTE: { + bool *mute = arg; 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); + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_set_playback_switch( + elem, SND_MIXER_SCHN_FRONT_RIGHT, !*mute); } - snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, !m_l); + snd_mixer_selem_set_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, + !*mute); break; } case AOCONTROL_GET_MUTE: { + bool *mute = arg; 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; + snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, + &tmp); + *mute = !tmp; + if (!snd_mixer_selem_has_playback_switch_joined(elem)) { + snd_mixer_selem_get_playback_switch( + elem, SND_MIXER_SCHN_FRONT_RIGHT, &tmp); + *mute &= !tmp; } break; } diff --git a/libao2/ao_oss.c b/libao2/ao_oss.c index 82a0dd51e5..9290a73380 100644 --- a/libao2/ao_oss.c +++ b/libao2/ao_oss.c @@ -179,22 +179,6 @@ static int volume_oss4(ao_control_vol_t *vol, int cmd) { // to set/get/query special features/parameters static int control(int cmd,void *arg){ switch(cmd){ - case AOCONTROL_SET_DEVICE: - dsp=(char*)arg; - return CONTROL_OK; - case AOCONTROL_GET_DEVICE: - *(char**)arg=dsp; - return CONTROL_OK; -#ifdef SNDCTL_DSP_GETFMTS - case AOCONTROL_QUERY_FORMAT: - { - int format; - if (!ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &format)) - if ((unsigned int)format & (unsigned long)arg) - return CONTROL_TRUE; - return CONTROL_FALSE; - } -#endif case AOCONTROL_GET_VOLUME: case AOCONTROL_SET_VOLUME: { diff --git a/libao2/ao_pulse.c b/libao2/ao_pulse.c index 5e71f7bf7e..ed6e08286a 100644 --- a/libao2/ao_pulse.c +++ b/libao2/ao_pulse.c @@ -23,7 +23,6 @@ #include <stdlib.h> #include <stdbool.h> #include <string.h> -#include <stdlib.h> #include <pulse/pulseaudio.h> @@ -422,18 +421,16 @@ static void info_func(struct pa_context *c, const struct pa_sink_input_info *i, pa_threaded_mainloop_signal(priv->mainloop, 0); } -static int control(struct ao *ao, int cmd, void *arg) +static int control(struct ao *ao, enum aocontrol cmd, void *arg) { struct priv *priv = ao->priv; switch (cmd) { - case AOCONTROL_GET_MUTE: // fallthrough + case AOCONTROL_GET_MUTE: case AOCONTROL_GET_VOLUME: { - ao_control_vol_t *vol = arg; uint32_t devidx = pa_stream_get_index(priv->stream); pa_threaded_mainloop_lock(priv->mainloop); if (!waitop(priv, pa_context_get_sink_input_info(priv->context, devidx, - info_func, ao))) - { + info_func, ao))) { GENERIC_ERR_MSG(priv->context, "pa_stream_get_sink_input_info() failed"); return CONTROL_ERROR; @@ -442,6 +439,7 @@ static int control(struct ao *ao, int cmd, void *arg) // we naively copied the struct, without updating pointers etc. // Pointers might point to invalid data, accessors might fail. if (cmd == AOCONTROL_GET_VOLUME) { + ao_control_vol_t *vol = arg; if (priv->pi.volume.channels != 2) vol->left = vol->right = pa_cvolume_avg(&priv->pi.volume) * 100 / PA_VOLUME_NORM; @@ -450,30 +448,30 @@ static int control(struct ao *ao, int cmd, void *arg) vol->right = priv->pi.volume.values[1] * 100 / PA_VOLUME_NORM; } } else if (cmd == AOCONTROL_GET_MUTE) { - vol->left = vol->right = priv->pi.mute ? 0.0f : 1.0f; + bool *mute = arg; + *mute = priv->pi.mute; } - return CONTROL_OK; - } + } - case AOCONTROL_SET_MUTE: // fallthrough + case AOCONTROL_SET_MUTE: case AOCONTROL_SET_VOLUME: { - const ao_control_vol_t *vol = arg; pa_operation *o; - struct pa_cvolume volume; - - pa_cvolume_reset(&volume, ao->channels); - if (volume.channels != 2) - pa_cvolume_set(&volume, volume.channels, - (pa_volume_t)vol->left * PA_VOLUME_NORM / 100); - else { - volume.values[0] = (pa_volume_t)vol->left * PA_VOLUME_NORM / 100; - volume.values[1] = (pa_volume_t)vol->right * PA_VOLUME_NORM / 100; - } pa_threaded_mainloop_lock(priv->mainloop); uint32_t stream_index = pa_stream_get_index(priv->stream); if (cmd == AOCONTROL_SET_VOLUME) { + const ao_control_vol_t *vol = arg; + struct pa_cvolume volume; + + pa_cvolume_reset(&volume, ao->channels); + if (volume.channels != 2) + pa_cvolume_set(&volume, volume.channels, + vol->left * PA_VOLUME_NORM / 100); + else { + volume.values[0] = vol->left * PA_VOLUME_NORM / 100; + volume.values[1] = vol->right * PA_VOLUME_NORM / 100; + } o = pa_context_set_sink_input_volume(priv->context, stream_index, &volume, NULL, NULL); if (!o) { @@ -483,9 +481,9 @@ static int control(struct ao *ao, int cmd, void *arg) return CONTROL_ERROR; } } else if (cmd == AOCONTROL_SET_MUTE) { - int mute = vol->left == 0.0f || vol->right == 0.0f; + const bool *mute = arg; o = pa_context_set_sink_input_mute(priv->context, stream_index, - mute, NULL, NULL); + *mute, NULL, NULL); if (!o) { pa_threaded_mainloop_unlock(priv->mainloop); GENERIC_ERR_MSG(priv->context, @@ -498,8 +496,7 @@ static int control(struct ao *ao, int cmd, void *arg) pa_operation_unref(o); pa_threaded_mainloop_unlock(priv->mainloop); return CONTROL_OK; - } - + } default: return CONTROL_UNKNOWN; } diff --git a/libao2/ao_sgi.c b/libao2/ao_sgi.c index 40bc6b9177..492c8ff3ba 100644 --- a/libao2/ao_sgi.c +++ b/libao2/ao_sgi.c @@ -118,13 +118,6 @@ static int control(int cmd, void *arg){ mp_tmsg(MSGT_AO, MSGL_INFO, "[AO SGI] control.\n"); - switch(cmd) { - case AOCONTROL_QUERY_FORMAT: - /* Do not reject any format: return the closest matching - * format if the request is not supported natively. */ - return CONTROL_TRUE; - } - return CONTROL_UNKNOWN; } diff --git a/libao2/ao_sun.c b/libao2/ao_sun.c index ef8417c5f9..ecdb23d4af 100644 --- a/libao2/ao_sun.c +++ b/libao2/ao_sun.c @@ -402,11 +402,6 @@ static void setup_device_paths(void) // to set/get/query special features/parameters static int control(int cmd,void *arg){ switch(cmd){ - case AOCONTROL_SET_DEVICE: - audio_dev=(char*)arg; - return CONTROL_OK; - case AOCONTROL_QUERY_FORMAT: - return CONTROL_TRUE; case AOCONTROL_GET_VOLUME: { int fd; diff --git a/libao2/audio_out.c b/libao2/audio_out.c index 6130e2ed33..268c17d749 100644 --- a/libao2/audio_out.c +++ b/libao2/audio_out.c @@ -232,7 +232,7 @@ int ao_play(struct ao *ao, void *data, int len, int flags) return ao->driver->play(ao, data, len, flags); } -int ao_control(struct ao *ao, int cmd, void *arg) +int ao_control(struct ao *ao, enum aocontrol cmd, void *arg) { if (ao->driver->control) return ao->driver->control(ao, cmd, arg); @@ -299,7 +299,7 @@ int old_ao_play(struct ao *ao, void *data, int len, int flags) return ao->driver->old_functions->play(data, len, flags); } -int old_ao_control(struct ao *ao, int cmd, void *arg) +int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg) { return ao->driver->old_functions->control(cmd, arg); } diff --git a/libao2/audio_out.h b/libao2/audio_out.h index aafedbf178..955376d460 100644 --- a/libao2/audio_out.h +++ b/libao2/audio_out.h @@ -23,6 +23,30 @@ #include "bstr.h" +#define CONTROL_OK 1 +#define CONTROL_TRUE 1 +#define CONTROL_FALSE 0 +#define CONTROL_UNKNOWN -1 +#define CONTROL_ERROR -2 +#define CONTROL_NA -3 + +enum aocontrol { + // _VOLUME commands take struct ao_control_vol pointer for input/output. + // If there's only one volume, SET should use average of left/right. + AOCONTROL_GET_VOLUME, + AOCONTROL_SET_VOLUME, + // _MUTE commands take a pointer to bool + AOCONTROL_GET_MUTE, + AOCONTROL_SET_MUTE, +}; + +#define AOPLAY_FINAL_CHUNK 1 + +typedef struct ao_control_vol { + float left; + float right; +} ao_control_vol_t; + typedef struct ao_info { /* driver name ("Matrox Millennium G200/G400" */ const char *name; @@ -53,7 +77,7 @@ struct ao_driver { bool is_new; const struct ao_info *info; const struct ao_old_functions *old_functions; - int (*control)(struct ao *ao, int cmd, void *arg); + int (*control)(struct ao *ao, enum aocontrol cmd, void *arg); int (*init)(struct ao *ao, char *params); void (*uninit)(struct ao *ao, bool cut_audio); void (*reset)(struct ao*ao); @@ -89,49 +113,18 @@ extern char *ao_subdevice; void list_audio_out(void); -#define CONTROL_OK 1 -#define CONTROL_TRUE 1 -#define CONTROL_FALSE 0 -#define CONTROL_UNKNOWN -1 -#define CONTROL_ERROR -2 -#define CONTROL_NA -3 - -#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 -// 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 - -typedef struct ao_control_vol { - float left; - float right; -} ao_control_vol_t; - struct ao *ao_create(struct MPOpts *opts, struct input_ctx *input); void ao_init(struct ao *ao, char **ao_list); void ao_uninit(struct ao *ao, bool cut_audio); int ao_play(struct ao *ao, void *data, int len, int flags); -int ao_control(struct ao *ao, int cmd, void *arg); +int ao_control(struct ao *ao, enum aocontrol cmd, void *arg); double ao_get_delay(struct ao *ao); int ao_get_space(struct ao *ao); void ao_reset(struct ao *ao); void ao_pause(struct ao *ao); void ao_resume(struct ao *ao); -int old_ao_control(struct ao *ao, int cmd, void *arg); +int old_ao_control(struct ao *ao, enum aocontrol cmd, void *arg); int old_ao_init(struct ao *ao, char *params); void old_ao_uninit(struct ao *ao, bool cut_audio); void old_ao_reset(struct ao*ao); diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c index f8311e215c..62cb0bad1b 100644 --- a/libmpdemux/demux_lavf.c +++ b/libmpdemux/demux_lavf.c @@ -417,8 +417,22 @@ static void handle_stream(demuxer_t *demuxer, AVFormatContext *avfc, int i) sh_video->video.dwRate = codec->time_base.den; sh_video->video.dwScale = codec->time_base.num; } - sh_video->fps = av_q2d(st->r_frame_rate); - sh_video->frametime = 1 / av_q2d(st->r_frame_rate); + /* Try to make up some frame rate value, even if it's not reliable. + * FPS information is needed to support subtitle formats which base + * timing on frame numbers. + * Libavformat seems to report no "reliable" FPS value for AVI files, + * while they are typically constant enough FPS that the value this + * heuristic makes up works with subtitles in practice. + */ + double fps; + if (st->r_frame_rate.num) + fps = av_q2d(st->r_frame_rate); + else + fps = 1.0 / FFMAX(av_q2d(st->time_base), + av_q2d(st->codec->time_base) * + st->codec->ticks_per_frame); + sh_video->fps = fps; + sh_video->frametime = 1 / fps; sh_video->format = bih->biCompression; if (st->sample_aspect_ratio.num) sh_video->aspect = codec->width * st->sample_aspect_ratio.num @@ -18,6 +18,8 @@ #include <string.h> +#include <libavutil/common.h> + #include "config.h" #include "libao2/audio_out.h" #include "libaf/af.h" @@ -25,243 +27,171 @@ #include "mixer.h" -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) +static void checkvolume(struct mixer *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) { - // restore previous volume (softvol, or no persistent AO volume) - internal_setvolume(mixer, mixer->restore_vol_l, mixer->restore_vol_r); - } - 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); - } -} -// 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 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. - // 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); - mixer->muted = true; - } -} - -static void internal_getvolume(mixer_t *mixer, float *l, float *r) -{ ao_control_vol_t vol; - *l = 0; - *r = 0; - if (mixer->ao) { - if (mixer->softvol || - 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, + if (mixer->softvol || CONTROL_OK != ao_control(mixer->ao, + AOCONTROL_GET_VOLUME, &vol)) { + mixer->softvol = true; + 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; - } - *r = vol.right; - *l = vol.left; + 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; } + float l = mixer->vol_l; + float r = mixer->vol_r; + if (mixer->muted_using_volume) + 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). + * We don't test for exact equality, as some AOs may round the value + * we last set to some nearby supported value. 3 has been the default + * volume step for increase/decrease keys, and is apparently big enough + * to step to the next possible value in most setups. + */ + 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) + 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; } -static float clip_vol(float v) +void mixer_getvolume(mixer_t *mixer, float *l, float *r) { - return v > 100 ? 100 : (v < 0 ? 0 : v); + checkvolume(mixer); + *l = mixer->vol_l; + *r = mixer->vol_r; } -static void internal_setvolume(mixer_t *mixer, float l, float r) +static void setvolume_internal(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; - if (mixer->ao) { - bool use_softvol = mixer->softvol; - 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; - 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, - 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") - && 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; - } + 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, + 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") + && 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"); } } 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) -{ - *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 { - *l = real_l; - *r = real_r; - } - } + checkvolume(mixer); // to check mute status and AO support for volume + mixer->vol_l = av_clip(l, 0, 100); + mixer->vol_r = av_clip(r, 0, 100); + if (!mixer->ao || mixer->muted) + return; + setvolume_internal(mixer, mixer->vol_l, mixer->vol_r); } -static void mixer_addvolume(mixer_t *mixer, float d) +void mixer_getbothvolume(mixer_t *mixer, float *b) { float mixer_l, mixer_r; mixer_getvolume(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); + *b = (mixer_l + mixer_r) / 2; } -void mixer_decvolume(mixer_t *mixer) +void mixer_setmute(struct mixer *mixer, bool mute) { - mixer_addvolume(mixer, -mixer->volstep); + 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 { + setvolume_internal(mixer, mixer->vol_l*!mute, mixer->vol_r*!mute); + mixer->muted_using_volume = mute; + } + mixer->muted = mute; + mixer->muted_by_us = mute; + } } -void mixer_getbothvolume(mixer_t *mixer, float *b) +bool mixer_getmute(struct mixer *mixer) { - float mixer_l, mixer_r; - mixer_getvolume(mixer, &mixer_l, &mixer_r); - *b = (mixer_l + mixer_r) / 2; + checkvolume(mixer); + return mixer->muted; } -void mixer_mute(mixer_t *mixer) +static void addvolume(struct mixer *mixer, float d) { - mixer_setmuted(mixer, !mixer_getmuted(mixer)); + checkvolume(mixer); + mixer_setvolume(mixer, mixer->vol_l + d, mixer->vol_r + d); + if (d > 0) + mixer_setmute(mixer, false); } -bool mixer_getmuted(mixer_t *mixer) +void mixer_incvolume(mixer_t *mixer) { - ao_control_vol_t vol = {0}; - if (!mixer->softvol && - 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; + addvolume(mixer, mixer->volstep); } -void mixer_setmuted(mixer_t *mixer, bool mute) +void mixer_decvolume(mixer_t *mixer) { - bool muted = mixer_getmuted(mixer); - if (mute == muted) - return; - ao_control_vol_t vol; - vol.left = vol.right = mute ? 0.0f : 1.0f; - mixer->mute_emulation = mixer->softvol || - 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; + addvolume(mixer, -mixer->volstep); } 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); + if (mixer->afilter) + af_control_any_rev(mixer->afilter, + AF_CONTROL_PAN_BALANCE | AF_CONTROL_GET, + &mixer->balance); + *val = mixer->balance; } +/* NOTE: Currently the balance code is seriously buggy: it always changes + * the af_pan mapping between the first two input channels and first two + * output channels to particular values. These values make sense for an + * af_pan instance that was automatically inserted for balance control + * only and is otherwise an identity transform, but if the filter was + * there for another reason, then ignoring and overriding the original + * values is completely wrong. In particular, this will break + * automatically inserted downmix filters; the original coefficients that + * are significantly below 1 will be overwritten with much higher values. + */ + void mixer_setbalance(mixer_t *mixer, float val) { float level[AF_NCH]; @@ -269,20 +199,21 @@ void mixer_setbalance(mixer_t *mixer, float val) af_control_ext_t arg_ext = { .arg = level }; af_instance_t *af_pan_balance; + mixer->balance = val; + if (!mixer->afilter) return; - mixer->balance = val; - mixer->restore_balance = true; - if (af_control_any_rev(mixer->afilter, AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val)) return; + if (val == 0 || mixer->ao->channels < 2) + return; + if (!(af_pan_balance = af_add(mixer->afilter, "pan"))) { mp_tmsg(MSGT_GLOBAL, MSGL_ERR, "[Mixer] No balance control available.\n"); - mixer->restore_balance = false; return; } @@ -301,3 +232,52 @@ void mixer_setbalance(mixer_t *mixer, float val) af_pan_balance->control(af_pan_balance, AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val); } + +// Called after the audio filter chain is built or rebuilt. +void mixer_reinit(struct mixer |