summaryrefslogtreecommitdiffstats
path: root/audio/filter
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 /audio/filter
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.
Diffstat (limited to 'audio/filter')
-rw-r--r--audio/filter/af.c4
-rw-r--r--audio/filter/af_rubberband.c169
2 files changed, 173 insertions, 0 deletions
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,
+ },
+};