From e094e9cb75f9802d8da07eb75cee4d91c9786558 Mon Sep 17 00:00:00 2001 From: wm4 Date: Mon, 10 Nov 2014 20:16:25 +0100 Subject: 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(). --- audio/decode/dec_audio.c | 1 - audio/filter/af.c | 2 + audio/filter/af.h | 1 + audio/filter/af_forcespeed.c | 69 +++++++++++++++++++++++++++++++++ old-makefile | 1 + player/audio.c | 90 +++++++++++++++++++++++--------------------- wscript_build.py | 1 + 7 files changed, 122 insertions(+), 43 deletions(-) create mode 100644 audio/filter/af_forcespeed.c diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c index 29674f3e0e..8b38b71bc7 100644 --- a/audio/decode/dec_audio.c +++ b/audio/decode/dec_audio.c @@ -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; diff --git a/audio/filter/af.c b/audio/filter/af.c index 6a3b4bf0d5..d396a73ce3 100644 --- a/audio/filter/af.c +++ b/audio/filter/af.c @@ -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, diff --git a/audio/filter/af.h b/audio/filter/af.h index b95039b4d0..5c7a6c0e7c 100644 --- a/audio/filter/af.h +++ b/audio/filter/af.h @@ -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 diff --git a/audio/filter/af_forcespeed.c b/audio/filter/af_forcespeed.c new file mode 100644 index 0000000000..d2d2f9abb7 --- /dev/null +++ b/audio/filter/af_forcespeed.c @@ -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 . + */ + +#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), +}; diff --git a/old-makefile b/old-makefile index d7d1b96be5..c7abacf96e 100644 --- a/old-makefile +++ b/old-makefile @@ -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 \ diff --git a/player/audio.c b/player/audio.c index fb3f571cdf..413cbf4a32 100644 --- a/player/audio.c +++ b/player/audio.c @@ -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; - - 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; + double speed = opts->playback_speed; + + 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); } diff --git a/wscript_build.py b/wscript_build.py index dfa0d7118b..a2c32494aa 100644 --- a/wscript_build.py +++ b/wscript_build.py @@ -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" ), -- cgit v1.2.3