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.
This commit is contained in:
wm4 2012-04-28 00:39:19 +02:00
commit 87f4cafe9c
14 changed files with 285 additions and 330 deletions

View File

@ -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)

View File

@ -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;
}

View File

@ -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:
{

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);

View File

@ -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

380
mixer.c
View File

@ -18,6 +18,8 @@
#include <string.h>
#include <libavutil/common.h>
#include "config.h"
#include "libao2/audio_out.h"
#include "libaf/af.h"
@ -25,185 +27,102 @@
#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;
}
}
}
static void mixer_addvolume(mixer_t *mixer, float d)
{
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);
}
void mixer_decvolume(mixer_t *mixer)
{
mixer_addvolume(mixer, -mixer->volstep);
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);
}
void mixer_getbothvolume(mixer_t *mixer, float *b)
@ -213,55 +132,66 @@ void mixer_getbothvolume(mixer_t *mixer, float *b)
*b = (mixer_l + mixer_r) / 2;
}
void mixer_mute(mixer_t *mixer)
void mixer_setmute(struct mixer *mixer, bool mute)
{
mixer_setmuted(mixer, !mixer_getmuted(mixer));
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;
}
}
bool mixer_getmuted(mixer_t *mixer)
bool mixer_getmute(struct mixer *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
}
checkvolume(mixer);
return mixer->muted;
}
void mixer_setmuted(mixer_t *mixer, bool mute)
static void addvolume(struct mixer *mixer, float d)
{
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;
checkvolume(mixer);
mixer_setvolume(mixer, mixer->vol_l + d, mixer->vol_r + d);
if (d > 0)
mixer_setmute(mixer, false);
}
void mixer_incvolume(mixer_t *mixer)
{
addvolume(mixer, mixer->volstep);
}
void mixer_decvolume(mixer_t *mixer)
{
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, 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);
if (mixer->balance != 0)
mixer_setbalance(mixer, mixer->balance);
}
/* Called before uninitializing the audio output. 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)
{
checkvolume(mixer);
if (mixer->muted_by_us) {
/* Current audio output API combines playing the remaining buffered
* audio and uninitializing the AO into one operation, even though
* ideally unmute would happen between those two steps. We can't do
* volume changes after uninitialization, but we don't want the
* remaining audio to play at full volume either. Thus this
* workaround to drop remaining audio first. */
ao_reset(mixer->ao);
mixer_setmute(mixer, false);
/* We remember mute status and re-enable it if we play more audio
* in the same process. */
mixer->muted_by_us = true;
}
mixer->ao = NULL;
}

22
mixer.h
View File

@ -28,27 +28,27 @@ typedef struct mixer {
struct ao *ao;
af_stream_t *afilter;
int volstep;
bool muted;
bool mute_emulation;
bool softvol;
float softvol_max;
float last_l, last_r;
float restore_vol_l, restore_vol_r;
bool restore_volume;
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 restore_balance;
} mixer_t;
void mixer_reinit(mixer_t *mixer);
void mixer_uninit(mixer_t *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_mute(mixer_t *mixer);
bool mixer_getmuted(mixer_t *mixer);
void mixer_setmuted(mixer_t *mixer, bool mute);
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);

View File

@ -1827,11 +1827,10 @@ void reinit_audio_chain(struct MPContext *mpctx)
"Couldn't find matching filter/ao format!\n");
goto init_error;
}
mpctx->mixer.ao = ao;
mpctx->mixer.volstep = volstep;
mpctx->mixer.softvol = opts->softvol;
mpctx->mixer.softvol_max = opts->softvol_max;
mixer_reinit(&mpctx->mixer);
mixer_reinit(&mpctx->mixer, ao);
mpctx->syncing_audio = true;
return;

View File

@ -89,7 +89,7 @@ int pvr_param_bitrate_peak = 0;
char *pvr_param_stream_type = NULL;
typedef struct station_elem_s {
char name[8];
char name[PVR_STATION_NAME_SIZE];
int freq;
char station[PVR_STATION_NAME_SIZE];
int enabled;

View File

@ -265,8 +265,13 @@ void mp_ass_configure_fonts(ASS_Renderer *priv)
path = strdup(sub_font_name);
else if (font_fontconfig < 0 && font_name)
path = strdup(font_name);
else
else {
path = get_path("subfont.ttf");
if (!mp_path_exists(path)) {
free(path);
path = NULL;
}
}
if (font_fontconfig >= 0 && sub_font_name)
family = strdup(sub_font_name);
else if (font_fontconfig >= 0 && font_name)