1
mirror of https://github.com/mpv-player/mpv synced 2024-09-12 23:45:53 +02:00
mpv/mixer.c
wm4 b338b16be7 audio: reset mplayer's mute state when the system mixer volume changes
Before this commit, the mute state was only reset when either mute was
explicitly cleared, or the volume was changed via mplayer controls. If
the volume controls are connected to the system mixer, and the system
mixer volume is changed otherwise (e.g. with alsamixer), the mute
setting was inconsistent.

Avoid this by checking the volume. If the returned volume is not 0, the
mute flag is considered invalid. This relies on system mixers always
returning a volume of 0 when mplayer has set the volume 0.

Possible caveat: if the audio output's volume control don't return a
volume of exactly 0 after 0 was written, enabling mute basically won't
work. It will set the volume to silence, forget the previous volume, and
report that mute is disabled.
2012-01-18 04:21:45 +01:00

281 lines
8.1 KiB
C

/*
* This file is part of MPlayer.
*
* MPlayer is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* MPlayer is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with MPlayer; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <string.h>
#ifndef __MINGW32__
#include <sys/ioctl.h>
#endif
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include "config.h"
#include "libao2/audio_out.h"
#include "libaf/af.h"
#include "mixer.h"
char *mixer_device = NULL;
char *mixer_channel = NULL;
int soft_vol = 0;
float soft_vol_max = 110.0;
// Called after the audio filter chain is built or rebuilt.
void mixer_reinit(mixer_t *mixer)
{
if (!mixer->ao)
return;
if (mixer->restore_volume) {
int muted = mixer->muted;
mixer_setvolume(mixer, mixer->restore_vol_l, mixer->restore_vol_r);
mixer->muted = muted;
}
if (mixer->muted) {
// undo mixer_uninit()
mixer_setvolume(mixer, 0, 0);
mixer->muted = true;
}
if (mixer->restore_balance) {
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 turn off the mute state when the player
// terminates. No other attempts at restoring the volume 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.
if (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 (soft_vol ||
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,
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] / (soft_vol_max / 100.0)) * 100.0;
vol.right = (db_vals[1] / (soft_vol_max / 100.0)) * 100.0;
}
*r = vol.right;
*l = vol.left;
}
}
static float clip_vol(float v)
{
return v > 100 ? 100 : (v < 0 ? 0 : v);
}
void mixer_setvolume(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 = soft_vol;
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) * (soft_vol_max / 100.0);
db_vals[1] = (r / 100.0) * (soft_vol_max / 100.0);
for (i = 2; i < AF_NCH; i++)
db_vals[i] = ((l + r) / 100.0) * (soft_vol_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;
}
}
mixer->muted = 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);
}
void mixer_getbothvolume(mixer_t *mixer, float *b)
{
float mixer_l, mixer_r;
mixer_getvolume(mixer, &mixer_l, &mixer_r);
*b = (mixer_l + mixer_r) / 2;
}
void mixer_mute(mixer_t *mixer)
{
mixer_setmuted(mixer, !mixer_getmuted(mixer));
}
bool mixer_getmuted(mixer_t *mixer)
{
// this call will also check whether mute is still active, and "fix" it
float l, r;
mixer_getvolume(mixer, &l, &r);
return mixer->muted;
}
void mixer_setmuted(mixer_t *mixer, bool mute)
{
bool muted = mixer_getmuted(mixer);
if (mute == muted)
return;
if (muted) {
mixer_setvolume(mixer, mixer->last_l, mixer->last_r);
} else {
mixer_getvolume(mixer, &mixer->last_l, &mixer->last_r);
mixer_setvolume(mixer, 0, 0);
mixer->muted = true;
}
}
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);
}
void mixer_setbalance(mixer_t *mixer, float val)
{
float level[AF_NCH];
int i;
af_control_ext_t arg_ext = { .arg = level };
af_instance_t *af_pan_balance;
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 (!(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;
}
af_init(mixer->afilter);
/* make all other channels pass thru since by default pan blocks all */
memset(level, 0, sizeof(level));
for (i = 2; i < AF_NCH; i++) {
arg_ext.ch = i;
level[i] = 1.f;
af_pan_balance->control(af_pan_balance,
AF_CONTROL_PAN_LEVEL | AF_CONTROL_SET,
&arg_ext);
level[i] = 0.f;
}
af_pan_balance->control(af_pan_balance,
AF_CONTROL_PAN_BALANCE | AF_CONTROL_SET, &val);
}