mirror of
https://github.com/mpv-player/mpv
synced 2024-09-09 01:16:56 +02:00
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:
commit
87f4cafe9c
10
command.c
10
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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
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,
|
||||
(pa_volume_t)vol->left * PA_VOLUME_NORM / 100);
|
||||
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;
|
||||
volume.values[0] = vol->left * PA_VOLUME_NORM / 100;
|
||||
volume.values[1] = 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) {
|
||||
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,
|
||||
@ -499,7 +497,6 @@ static int control(struct ao *ao, int cmd, void *arg)
|
||||
pa_threaded_mainloop_unlock(priv->mainloop);
|
||||
return CONTROL_OK;
|
||||
}
|
||||
|
||||
default:
|
||||
return CONTROL_UNKNOWN;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
324
mixer.c
324
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,107 +27,70 @@
|
||||
#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->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 {
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (use_softvol) {
|
||||
mixer->restore_volume = "softvol";
|
||||
if (!mixer->afilter)
|
||||
return;
|
||||
// af_volume uses values in dB
|
||||
@ -134,76 +99,30 @@ static void internal_setvolume(mixer_t *mixer, float l, float r)
|
||||
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;
|
||||
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))
|
||||
{
|
||||
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)))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
}
|
||||
mixer->muted = mute;
|
||||
|
||||
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
22
mixer.h
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user