summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--command.c10
-rw-r--r--libao2/ao_alsa.c33
-rw-r--r--libao2/ao_oss.c16
-rw-r--r--libao2/ao_pulse.c47
-rw-r--r--libao2/ao_sgi.c7
-rw-r--r--libao2/ao_sun.c5
-rw-r--r--libao2/audio_out.c4
-rw-r--r--libao2/audio_out.h61
-rw-r--r--libmpdemux/demux_lavf.c18
-rw-r--r--mixer.c374
-rw-r--r--mixer.h22
-rw-r--r--mplayer.c3
-rw-r--r--stream/stream_pvr.c2
-rw-r--r--sub/ass_mp.c7
14 files changed, 282 insertions, 327 deletions
diff --git a/command.c b/command.c
index b04fe09656..a7446cb855 100644
--- a/command.c
+++ b/command.c
@@ -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
diff --git a/mixer.c b/mixer.c
index d115dc3e20..7be2179384 100644
--- a/mixer.c
+++ b/mixer.c
@@ -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 *mixer, struct ao *ao)
+{
+ 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,