audio: change how filters are inserted on playback speed changes

Use a pseudo-filter when changing speed with resampling, instead of
somehow changing a samplerate somewhere. This uses the same underlying
mechanism, but is a bit more structured and cleaner. It also makes some
of the following changes easier.

Since we now always use filters to change audio speed, move most of the
work set_playback_speed() does to recreate_audio_filters().
This commit is contained in:
wm4 2014-11-10 20:16:25 +01:00
parent 995a6af787
commit e094e9cb75
7 changed files with 121 additions and 42 deletions

View File

@ -227,7 +227,6 @@ static int filter_n_bytes(struct dec_audio *da, struct mp_audio_buffer *outbuf,
// Filter
struct mp_audio filter_data;
mp_audio_buffer_peek(da->decode_buffer, &filter_data);
filter_data.rate = da->afilter->input.rate; // due to playback speed change
len = MPMIN(filter_data.samples, len);
filter_data.samples = len;
bool eof = error == AD_EOF && filter_data.samples == 0;

View File

@ -53,6 +53,7 @@ extern const struct af_info af_info_center;
extern const struct af_info af_info_sinesuppress;
extern const struct af_info af_info_karaoke;
extern const struct af_info af_info_scaletempo;
extern const struct af_info af_info_forcespeed;
extern const struct af_info af_info_bs2b;
extern const struct af_info af_info_lavfi;
extern const struct af_info af_info_convert24;
@ -83,6 +84,7 @@ static const struct af_info *const filter_list[] = {
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
&af_info_forcespeed,
&af_info_scaletempo,
#if HAVE_LIBBS2B
&af_info_bs2b,

View File

@ -120,6 +120,7 @@ enum af_control {
AF_CONTROL_SET_PAN_BALANCE,
AF_CONTROL_GET_PAN_BALANCE,
AF_CONTROL_SET_PLAYBACK_SPEED,
AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE,
};
// Argument for AF_CONTROL_SET_PAN_LEVEL

View File

@ -0,0 +1,69 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "af.h"
struct priv {
double speed;
};
static int control(struct af_instance *af, int cmd, void *arg)
{
struct priv *priv = af->priv;
switch (cmd) {
case AF_CONTROL_REINIT: {
struct mp_audio *in = arg;
struct mp_audio orig_in = *in;
struct mp_audio *out = af->data;
mp_audio_copy_config(out, in);
out->rate = in->rate * priv->speed;
return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
}
case AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE: {
priv->speed = *(double *)arg;
return AF_OK;
}
}
return AF_UNKNOWN;
}
static int filter(struct af_instance *af, struct mp_audio *data, int flags)
{
mp_audio_copy_config(data, af->data);
return 0;
}
static int af_open(struct af_instance *af)
{
struct priv *priv = af->priv;
af->control = control;
af->filter = filter;
priv->speed = 1.0;
return AF_OK;
}
#define OPT_BASE_STRUCT struct priv
const struct af_info af_info_forcespeed = {
.info = "Force audio speed",
.name = "forcespeed",
.open = af_open,
.priv_size = sizeof(struct priv),
};

View File

@ -127,6 +127,7 @@ SOURCES = audio/audio.c \
audio/filter/af_equalizer.c \
audio/filter/af_export.c \
audio/filter/af_extrastereo.c \
audio/filter/af_forcespeed.c \
audio/filter/af_format.c \
audio/filter/af_hrtf.c \
audio/filter/af_karaoke.c \

View File

@ -43,25 +43,61 @@
#include "core.h"
#include "command.h"
static int try_filter(struct MPContext *mpctx,
char *name, char *label, char **args)
{
struct dec_audio *d_audio = mpctx->d_audio;
if (af_find_by_label(d_audio->afilter, label))
return 0;
struct af_instance *af = af_add(d_audio->afilter, name, args);
if (!af)
return -1;
af->label = talloc_strdup(af, label);
return 1;
}
static int recreate_audio_filters(struct MPContext *mpctx)
{
assert(mpctx->d_audio);
struct af_stream *afs = mpctx->d_audio->afilter;
struct MPOpts *opts = mpctx->opts;
struct af_stream *afs = mpctx->d_audio->afilter;
struct mp_audio in_format;
mp_audio_buffer_get_format(mpctx->d_audio->decode_buffer, &in_format);
int new_srate = in_format.rate;
double speed = opts->playback_speed;
if (!af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED,
&opts->playback_speed))
{
new_srate = in_format.rate * opts->playback_speed;
if (new_srate != afs->output.rate)
opts->playback_speed = new_srate / (double)in_format.rate;
if (speed != 1.0) {
int method = AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE;
if (speed > 1.0 && opts->pitch_correction)
method = AF_CONTROL_SET_PLAYBACK_SPEED;
if (!af_control_any_rev(afs, method, &speed)) {
if (af_remove_by_label(afs, "playback-speed") < 0)
return -1;
// Compatibility: if the user uses --af=scaletempo, always use
// this filter to change speed. Don't insert a second "scaletempo"
// filter either.
if (!af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED, &speed))
{
char *filter = method == AF_CONTROL_SET_PLAYBACK_SPEED
? "scaletempo" : "forcespeed";
if (try_filter(mpctx, filter, "playback-speed", NULL) < 0)
return -1;
// Try again.
if (!af_control_any_rev(afs, method, &speed))
return -1;
}
}
} else {
if (af_remove_by_label(afs, "playback-speed") < 0)
return -1;
// The filters could be inserted by the user (we don't remove them).
af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED, &speed);
af_control_any_rev(afs, AF_CONTROL_SET_PLAYBACK_SPEED_RESAMPLE, &speed);
}
afs->input.rate = new_srate;
if (af_init(afs) < 0) {
MP_ERR(mpctx, "Couldn't find matching filter/ao format!\n");
@ -88,23 +124,6 @@ int reinit_audio_filters(struct MPContext *mpctx)
return 1;
}
static int try_filter(struct MPContext *mpctx,
char *name, char *label, char **args)
{
struct dec_audio *d_audio = mpctx->d_audio;
if (af_find_by_label(d_audio->afilter, label))
return 0;
struct af_instance *af = af_add(d_audio->afilter, name, args);
if (!af)
return -1;
af->label = talloc_strdup(af, label);
return 1;
}
void set_playback_speed(struct MPContext *mpctx, double new_speed)
{
struct MPOpts *opts = mpctx->opts;
@ -117,19 +136,6 @@ void set_playback_speed(struct MPContext *mpctx, double new_speed)
if (!mpctx->d_audio)
return;
if (new_speed > 1.0 && opts->pitch_correction) {
if (!af_control_any_rev(mpctx->d_audio->afilter,
AF_CONTROL_SET_PLAYBACK_SPEED,
&new_speed))
{
if (try_filter(mpctx, "scaletempo", "playback-speed", NULL) < 0)
return;
}
} else {
if (af_remove_by_label(mpctx->d_audio->afilter, "playback-speed") < 0)
return;
}
recreate_audio_filters(mpctx);
}

View File

@ -114,6 +114,7 @@ def build(ctx):
( "audio/filter/af_equalizer.c" ),
( "audio/filter/af_export.c", "sys-mman-h" ),
( "audio/filter/af_extrastereo.c" ),
( "audio/filter/af_forcespeed.c" ),
( "audio/filter/af_format.c" ),
( "audio/filter/af_hrtf.c" ),
( "audio/filter/af_karaoke.c" ),