summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2015-02-10 23:50:17 +0100
committerwm4 <wm4@nowhere>2015-02-11 00:29:12 +0100
commitb6ab34fc98a4f839a505d3410b87a40286ed2ccc (patch)
tree5236ad097fae9b3dce38c04c7b99c827dde0adc5
parentabbaaaa6e7b565758822fb6aa90ebbb10353f699 (diff)
downloadmpv-b6ab34fc98a4f839a505d3410b87a40286ed2ccc.tar.bz2
mpv-b6ab34fc98a4f839a505d3410b87a40286ed2ccc.tar.xz
af_rubberband: pitch correction with librubberband
If "--af=rubberband" is used, librubberband will be used to speed up or slow down audio with pitch correction. This still has some problems: the audio delay is not calculated correctly, so the audio position jitters around by a few milliseconds. This will probably ruin video timing.
-rw-r--r--DOCS/man/af.rst5
-rw-r--r--audio/filter/af.c4
-rw-r--r--audio/filter/af_rubberband.c169
-rwxr-xr-xold-configure3
-rw-r--r--old-makefile1
-rw-r--r--wscript4
-rw-r--r--wscript_build.py1
7 files changed, 187 insertions, 0 deletions
diff --git a/DOCS/man/af.rst b/DOCS/man/af.rst
index 6983041bef..ffb6c03e98 100644
--- a/DOCS/man/af.rst
+++ b/DOCS/man/af.rst
@@ -600,6 +600,11 @@ Available filters are:
Changing playback speed would change pitch, leaving audio tempo at
1.2x.
+``rubberband``
+ High quality pitch correction with librubberband. This can be used in place
+ of ``scaletempo``, and will be used to adjust audio pitch when playing
+ at speed different from normal.
+
``lavfi=graph``
Filter audio using FFmpeg's libavfilter.
diff --git a/audio/filter/af.c b/audio/filter/af.c
index dc534efa5b..bde469db8b 100644
--- a/audio/filter/af.c
+++ b/audio/filter/af.c
@@ -59,6 +59,7 @@ extern const struct af_info af_info_bs2b;
extern const struct af_info af_info_lavfi;
extern const struct af_info af_info_convert24;
extern const struct af_info af_info_convertsignendian;
+extern const struct af_info af_info_rubberband;
static const struct af_info *const filter_list[] = {
&af_info_dummy,
@@ -80,6 +81,9 @@ static const struct af_info *const filter_list[] = {
#if HAVE_LADSPA
&af_info_ladspa,
#endif
+#if HAVE_RUBBERBAND
+ &af_info_rubberband,
+#endif
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
diff --git a/audio/filter/af_rubberband.c b/audio/filter/af_rubberband.c
new file mode 100644
index 0000000000..551461d326
--- /dev/null
+++ b/audio/filter/af_rubberband.c
@@ -0,0 +1,169 @@
+/*
+ * 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 <stdlib.h>
+#include <assert.h>
+
+#include <rubberband/rubberband-c.h>
+
+#include "common/common.h"
+#include "af.h"
+
+struct priv {
+ RubberBandState rubber;
+ double speed;
+ struct mp_audio *pending;
+ bool needs_reset;
+};
+
+static void update_speed(struct af_instance *af, double new_speed)
+{
+ struct priv *p = af->priv;
+
+ p->speed = new_speed;
+ rubberband_set_time_ratio(p->rubber, 1.0 / p->speed);
+}
+
+static int control(struct af_instance *af, int cmd, void *arg)
+{
+ struct priv *p = 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;
+
+ in->format = AF_FORMAT_FLOATP;
+ mp_audio_copy_config(out, in);
+
+ if (p->rubber)
+ rubberband_delete(p->rubber);
+
+ int opts = RubberBandOptionProcessRealTime
+ | RubberBandOptionStretchPrecise
+ | RubberBandOptionSmoothingOn
+ | RubberBandOptionPitchHighConsistency;
+
+ p->rubber = rubberband_new(in->rate, in->channels.num, opts, 1.0, 1.0);
+ if (!p->rubber) {
+ MP_FATAL(af, "librubberband initialization failed.\n");
+ return AF_ERROR;
+ }
+
+ update_speed(af, p->speed);
+
+ return mp_audio_config_equals(in, &orig_in) ? AF_OK : AF_FALSE;
+ }
+ case AF_CONTROL_SET_PLAYBACK_SPEED: {
+ update_speed(af, *(double *)arg);
+ return AF_OK;
+ }
+ case AF_CONTROL_RESET:
+ if (p->rubber)
+ rubberband_reset(p->rubber);
+ talloc_free(p->pending);
+ p->pending = NULL;
+ return AF_OK;
+ }
+ return AF_UNKNOWN;
+}
+
+static int filter_frame(struct af_instance *af, struct mp_audio *data)
+{
+ struct priv *p = af->priv;
+
+ talloc_free(p->pending);
+ p->pending = data;
+
+ return 0;
+}
+
+static int filter_out(struct af_instance *af)
+{
+ struct priv *p = af->priv;
+
+ if (p->needs_reset)
+ rubberband_reset(p->rubber);
+ p->needs_reset = false;
+
+ while (!rubberband_available(p->rubber)) {
+ const float *dummy[MP_NUM_CHANNELS] = {0};
+ const float **in_data = dummy;
+ size_t in_samples = 0;
+ if (p->pending) {
+ if (!p->pending->samples)
+ return 0;
+ size_t needs = rubberband_get_samples_required(p->rubber);
+ in_data = (void *)&p->pending->planes;
+ in_samples = MPMIN(p->pending->samples, needs);
+ }
+ p->needs_reset = !p->pending; // EOF
+ rubberband_process(p->rubber, in_data, in_samples, p->needs_reset);
+ if (!p->pending)
+ break;
+ mp_audio_skip_samples(p->pending, in_samples);
+ }
+
+ size_t out_samples = rubberband_available(p->rubber);
+ if (!out_samples)
+ return 0;
+
+ struct mp_audio *out =
+ mp_audio_pool_get(af->out_pool, af->data, out_samples);
+ if (!out)
+ return -1;
+ if (p->pending)
+ mp_audio_copy_config(out, p->pending);
+ float **out_data = (void *)&out->planes;
+ out->samples = rubberband_retrieve(p->rubber, out_data, out->samples);
+ af_add_output_frame(af, out);
+
+ int delay = rubberband_get_latency(p->rubber);
+ delay += p->pending ? p->pending->samples : 0;
+ af->delay = delay / (double)af->data->rate;
+
+ return 0;
+}
+
+static void uninit(struct af_instance *af)
+{
+ struct priv *p = af->priv;
+
+ if (p->rubber)
+ rubberband_delete(p->rubber);
+ talloc_free(p->pending);
+}
+
+static int af_open(struct af_instance *af)
+{
+ af->control = control;
+ af->filter_frame = filter_frame;
+ af->filter_out = filter_out;
+ af->uninit = uninit;
+ return AF_OK;
+}
+
+const struct af_info af_info_rubberband = {
+ .info = "Pitch conversion with librubberband",
+ .name = "rubberband",
+ .open = af_open,
+ .priv_size = sizeof(struct priv),
+ .priv_defaults = &(const struct priv) {
+ .speed = 1.0,
+ },
+};
diff --git a/old-configure b/old-configure
index 503f3d5d38..dc78863007 100755
--- a/old-configure
+++ b/old-configure
@@ -183,6 +183,7 @@ options_state_machine() {
opt_yes_no _libavfilter "libavfilter"
opt_yes_no _jpeg "support for writing JPEG screenshots"
opt_yes_no _libcdio "libcdio support"
+ opt_yes_no _librubberband "librubberband support"
opt_yes_no _ffmpeg "skip FFmpeg/Libav autodetection"
opt_yes_no _ladspa "LADSPA plugin support"
opt_yes_no _libbs2b "libbs2b audio filter support"
@@ -717,6 +718,8 @@ check_pkg_config "dvdnav" $_dvdnav DVDNAV 'dvdnav >= 4.2.0'
check_pkg_config "libcdio" $_libcdio CDDA 'libcdio_paranoia'
+check_pkg_config "rubberband" $_librubberband RUBBERBAND 'rubberband'
+
_oldass=$_libass
check_pkg_config "SSA/ASS support" $_libass LIBASS 'libass'
_libass=$(defretval)
diff --git a/old-makefile b/old-makefile
index 50d7710484..0aec7b4b73 100644
--- a/old-makefile
+++ b/old-makefile
@@ -36,6 +36,7 @@ SOURCES-$(DVDNAV) += stream/stream_dvdnav.c \
stream/stream_dvd_common.c
SOURCES-$(LADSPA) += audio/filter/af_ladspa.c
+SOURCES-$(RUBBERBAND) += audio/filter/af_rubberband.c
SOURCES-$(LIBASS) += sub/ass_mp.c sub/sd_ass.c \
demux/demux_libass.c
diff --git a/wscript b/wscript
index 8f3961cf97..e7e939a629 100644
--- a/wscript
+++ b/wscript
@@ -336,6 +336,10 @@ If you really mean to compile without libass support use --disable-libass."
'desc': 'LADSPA plugin support',
'func': check_statement('ladspa.h', 'LADSPA_Descriptor ld = {0}'),
}, {
+ 'name': '--rubberband',
+ 'desc': 'librubberband support',
+ 'func': check_pkg_config('rubberband', '>= 1.8.0'),
+ }, {
'name': '--libbs2b',
'desc': 'libbs2b audio filter support',
'func': check_pkg_config('libbs2b'),
diff --git a/wscript_build.py b/wscript_build.py
index 565dbbc3b1..ab92cd3466 100644
--- a/wscript_build.py
+++ b/wscript_build.py
@@ -118,6 +118,7 @@ def build(ctx):
( "audio/filter/af_lavfi.c", "libavfilter" ),
( "audio/filter/af_lavrresample.c" ),
( "audio/filter/af_pan.c" ),
+ ( "audio/filter/af_rubberband.c", "rubberband" ),
( "audio/filter/af_scaletempo.c" ),
( "audio/filter/af_sinesuppress.c" ),
( "audio/filter/af_sub.c" ),