[MPlayer-dev-eng] [PATCH 3/7] af_scale*: af_scaletempo
Robert Juliano
juliano.1 at osu.edu
Mon Jun 11 06:48:46 CEST 2007
af_scaletempo
This is the main patch. It adds a filter that, when used
(-af scaletempo), syncs with playback_speed to scale the audio
tempo while preserving the pitch.
Files:
libaf/Makefile
libaf/af.c
libaf/af_scaletempo.c
libaf/control.h
Index: mplayer-HEAD/libaf/Makefile
===================================================================
--- mplayer-HEAD.orig/libaf/Makefile 2007-06-10 16:33:13.000000000 -0400
+++ mplayer-HEAD/libaf/Makefile 2007-06-10 16:35:02.000000000 -0400
@@ -16,6 +16,7 @@
af_karaoke.c \
af_pan.c \
af_resample.c \
+ af_scaletempo.c \
af_sinesuppress.c \
af_sub.c \
af_surround.c \
Index: mplayer-HEAD/libaf/af.c
===================================================================
--- mplayer-HEAD.orig/libaf/af.c 2007-06-10 16:34:57.000000000 -0400
+++ mplayer-HEAD/libaf/af.c 2007-06-10 16:35:02.000000000 -0400
@@ -31,6 +31,7 @@
extern af_info_t af_info_center;
extern af_info_t af_info_sinesuppress;
extern af_info_t af_info_karaoke;
+extern af_info_t af_info_scaletempo;
static af_info_t* filter_list[]={
&af_info_dummy,
@@ -61,6 +62,7 @@
&af_info_center,
&af_info_sinesuppress,
&af_info_karaoke,
+ &af_info_scaletempo,
NULL
};
Index: mplayer-HEAD/libaf/af_scaletempo.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ mplayer-HEAD/libaf/af_scaletempo.c 2007-06-10 16:52:31.000000000 -0400
@@ -0,0 +1,323 @@
+/*
+ * (cc) GPL 2007 MPlayer / Robert Juliano
+ * Inspired by SoundTouch library by Olli Parviainen
+ *
+ * License: GPL http://creativecommons.org/licenses/GPL/2.0/
+ *
+ * scale tempo while maintaining pitch
+ * (WSOLA technique with cross correlation)
+ *
+ * basic algorithm
+ * - produce 'stride' output samples per loop
+ * - consume stride*speed input samples per loop
+ *
+ * to produce smoother transitions between strides, blend next ns_overlap
+ * samples from last stride with correlated samples of current input
+ *
+ * Changelog
+ * 2007-06-10 Initial release
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "af.h"
+
+#define AOF(a, i) &(((float *)a)[i])
+// Weee! pointer arithmatic!
+//#define AOF(a, i) ((float *)a + i)
+
+// Data for specific instances of this filter
+typedef struct af_scaletempo_s
+{
+ float speed;
+ int np_window;
+ int np_overlap;
+ int np_seek;
+ int ns_stride;
+ float ns_stride_scaled;
+ float ns_stride_error;
+ int ns_standing;
+ int ns_min_win;
+ float* buf_win;
+ int ns_win; // number of samples in window buffer
+ int ns_towin; // number of samples until next window
+ float* buf_ovl;
+ float* buf_calc; // reusable memory for best_overlap_calculation
+} af_scaletempo_t;
+
+int fill_window(struct af_instance_s* af, af_data_t* c, int ic) {
+ af_scaletempo_t* s = (af_scaletempo_t*)af->setup;
+ int ns_in = c->len/c->bps - ic;
+ int ic0 = ic;
+ int bps = af->data->bps;
+ float* win = s->buf_win;
+
+ // skip
+ if (s->ns_towin > 0) {
+ if (s->ns_towin < s->ns_win) {
+ int ns_tomove = s->ns_win - s->ns_towin;
+ memmove(win, &win[s->ns_towin], ns_tomove * bps);
+ s->ns_win = ns_tomove;
+ s->ns_towin = 0;
+ } else {
+ int ns_toskip;
+ s->ns_towin -= s->ns_win;
+ ns_toskip = min(s->ns_towin, ns_in);
+ s->ns_towin -= ns_toskip;
+ ic += ns_toskip;
+ ns_in -= ns_toskip;
+ s->ns_win = 0;
+ }
+ }
+
+ // copy
+ if (ns_in > 0) {
+ int ns_tocopy = min(s->ns_min_win - s->ns_win, ns_in);
+ if (ns_tocopy < 0) af_msg(AF_MSG_FATAL, "[scaletempo] Yes, it can happen!\nPlease submit bug report: libaf/af_scaletempo.c:%i needs fixed\n", __LINE__);
+ memcpy(win + s->ns_win, AOF(c->audio, ic), ns_tocopy*bps);
+ s->ns_win += ns_tocopy;
+ ic += ns_tocopy;
+ ns_in -= ns_tocopy;
+ }
+
+ return ic - ic0;
+}
+
+int best_overlap_off(struct af_instance_s* af) {
+ af_scaletempo_t* s = (af_scaletempo_t*)af->setup;
+
+ int np_off = 0;
+ float best_corr = INT_MIN;
+ register int i, j, io, iw;
+ int ib, is;
+
+ int nch = af->data->nch;
+ int np_overlap = s->np_overlap;
+ int ns_overlap = np_overlap * nch;
+ int np_seek = s->np_seek;
+
+ float* win = s->buf_win;
+ float* ovl = s->buf_ovl;
+ float* calc = s->buf_calc;
+
+ // precalc
+ for (i=0, io=0; i<np_overlap; i++) {
+ float t = i * (np_overlap - i);
+ for (j=0; j<nch; j++, io++) {
+ calc[io] = ovl[io] * t;
+ }
+ }
+
+ for (i=0, is=0; i<np_seek; i++, is += nch) {
+ float corr = 0;
+ iw = is;
+ for (ib=0; ib<ns_overlap; ib++, iw++) {
+ corr += calc[ib] * win[iw];
+ }
+
+ if (corr > best_corr) {
+ best_corr = corr;
+ np_off = i;
+ }
+ }
+
+ return np_off * nch;
+}
+
+// Filter data through filter
+static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+{
+ af_scaletempo_t* s = (af_scaletempo_t*)af->setup;
+ af_data_t* c = data; // Current working data
+ af_data_t* l = af->data; // Local data
+ int ns_in = c->len/c->bps; // Length of input in samples
+ int nch = c->nch; // Number of channels
+ int bps = c->bps; // bytes per sample
+ int ic, il;
+ int nb_out;
+
+ if (s->speed == 1.0) return c;
+
+ // RESIZE_LOCAL_BUFFER - can't use macro
+ // number of strides in input * size of output stride
+ nb_out = (int) ((ns_in / s->ns_stride_scaled + 1) * s->ns_stride * bps + 1);
+ if (nb_out > l->len) {
+ if (nb_out < l->len * 1.25) nb_out *= 1.25;
+ af_msg(AF_MSG_VERBOSE,"[libaf] Reallocating memory in module %s, "
+ "old len = %i, new len = %i\n",af->info->name,l->len,nb_out);
+ if (l->audio)
+ free(l->audio);
+ l->audio = malloc(nb_out);
+ if (!l->audio) {
+ af_msg(AF_MSG_FATAL,"[libaf] Could not allocate memory\n");
+ return NULL;
+ }
+ l->len = nb_out;
+ }
+
+ ic = 0;
+ il = 0;
+ ic = fill_window(af, c, 0);
+ while (s->ns_win >= s->ns_min_win) {
+ int io, iw;
+ float tf;
+ float ti;
+ register int i, j;
+ int ns_off = best_overlap_off(af);
+
+ // output overlap
+ for (i=0, io=0, iw=ns_off; i<s->np_overlap; i++) {
+ float t = i / (float)s->np_overlap;
+ for (j=0; j<nch; j++, il++, io++, iw++) {
+ ((float *)l->audio)[il] = (1 - t) * s->buf_ovl[io] + t * s->buf_win[iw];
+ }
+ }
+
+ // output standing
+ iw = ns_off + s->np_overlap * af->data->nch;
+ memcpy(AOF(l->audio, il), AOF(s->buf_win, iw), s->ns_standing*bps);
+ il += s->ns_standing;
+
+ // input stride
+ tf = s->ns_stride_scaled + s->ns_stride_error;
+ ti = (int)(tf + .5); // round
+ s->ns_stride_error = tf - ti;
+ s->ns_towin = ti;
+
+ // update overlap buffer
+ io = ns_off + s->ns_stride;
+ memcpy(s->buf_ovl, AOF(s->buf_win, io), s->np_overlap*nch*bps);
+
+ // loop
+ ic += fill_window(af, c, ic);
+ }
+
+ c->audio = l->audio;
+ c->len = il*bps;
+ return c;
+}
+
+// Initialization and runtime control
+static int control(struct af_instance_s* af, int cmd, void* arg)
+{
+ af_scaletempo_t* s = (af_scaletempo_t*)af->setup;
+ switch(cmd){
+ case AF_CONTROL_REINIT:{
+ af_data_t* c = (af_data_t*)arg;
+ float srate = c->rate / 1000;
+ int nch = c->nch;
+
+ af->data->rate = c->rate;
+ af->data->nch = nch;
+ af->data->format = AF_FORMAT_FLOAT_NE;
+ af->data->bps = 4;
+
+ // set through experimentation - same as SoundTouch
+ s->np_seek = srate * 14;
+ s->np_window = srate * 82;
+ s->np_overlap = srate * 12;
+
+ s->ns_stride = (s->np_window - s->np_overlap) * nch;
+ s->ns_stride_scaled = s->ns_stride * s->speed;
+ s->ns_standing = s->ns_stride - s->np_overlap * nch;
+ s->ns_min_win = (s->np_seek + s->np_window) * nch;
+
+ af_msg(AF_MSG_VERBOSE,"[scaletempo] set s->speed = %5.2f\t%8.3f ss\n", s->speed, s->ns_stride_scaled);
+
+ // set af->mul.[nd]
+ af->mul.n = s->ns_stride;
+ af->mul.d = s->ns_stride_scaled;
+ af_frac_cancel(&af->mul);
+
+ // allocate buffers
+ if (!s->buf_win) {
+ s->buf_win = calloc(s->ns_min_win, sizeof(float));
+ if(!s->buf_win)
+ af_msg(AF_MSG_FATAL,"[scaletempo] Out of memory\n");
+ }
+
+ if (!s->buf_ovl) {
+ s->buf_ovl = calloc(s->np_overlap * nch, sizeof(float));
+ if(!s->buf_ovl)
+ af_msg(AF_MSG_FATAL,"[scaletempo] Out of memory\n");
+ }
+
+ if (!s->buf_calc) {
+ s->buf_calc = calloc(s->np_overlap * nch, sizeof(float));
+ if (!s->buf_calc)
+ af_msg(AF_MSG_FATAL,"[scaletempo] Out of memory\n");
+ }
+
+ af_msg(AF_MSG_DEBUG1, "[scaletempo] %6i rate, %1i nch, %1i bps, %s format\n", af->data->rate, af->data->nch, af->data->bps, af_fmt2str_short(af->data->format));
+ af_msg(AF_MSG_DEBUG0, "[scaletempo] %.2f speed, %5is s, %5.3fs ss, %.3fs se, %6is min_win\n", s->speed, s->ns_stride, s->ns_stride_scaled, s->ns_stride_error, s->ns_min_win);
+ af_msg(AF_MSG_DEBUG0, "[scaletempo] %5ip (%5is) seek, %5ip (%5is) win, %5ip (%5is) ovl, %5is stand\n", s->np_seek, s->np_seek*nch, s->np_window, s->np_window*nch, s->np_overlap, s->np_overlap*nch, s->ns_standing);
+
+ return af_test_output(af,(af_data_t*)arg);
+ }
+ case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:
+ case AF_CONTROL_PLAYBACK_SPEED | AF_CONTROL_SET:
+ s->speed = *(float*)arg;
+ return AF_OK;
+ case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET:{
+ *(float*)arg = s->speed;
+ return AF_OK;
+ }
+ }
+ return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+ af_scaletempo_t* s = (af_scaletempo_t *)af->setup;
+
+ if (af->data)
+ free(af->data->audio);
+ free(af->data);
+
+ if(s->buf_win)
+ free(s->buf_win);
+
+ if(s->buf_ovl)
+ free(s->buf_ovl);
+
+ if (s->buf_calc)
+ free(s->buf_calc);
+
+ free(af->setup);
+}
+
+// Allocate memory and set function pointers
+static int af_open(af_instance_t* af){
+ af_scaletempo_t* s;
+
+ af->control = control;
+ af->uninit = uninit;
+ af->play = play;
+ af->mul.n = 1;
+ af->mul.d = 1;
+ af->data = calloc(1,sizeof(af_data_t));
+ af->setup = calloc(1,sizeof(af_scaletempo_t));
+ if(af->data == NULL || af->setup == NULL)
+ return AF_ERROR;
+
+ s = (af_scaletempo_t*)af->setup;
+ s->speed = 1.0;
+ s->ns_stride_error = 0;
+
+ return AF_OK;
+}
+
+// Description of this filter
+af_info_t af_info_scaletempo = {
+ "Scale audio tempo while maintaining pitch",
+ "scaletempo",
+ "Robert Juliano",
+ "",
+ AF_FLAGS_NOT_REENTRANT,
+ af_open
+};
Index: mplayer-HEAD/libaf/control.h
===================================================================
--- mplayer-HEAD.orig/libaf/control.h 2007-06-10 16:33:13.000000000 -0400
+++ mplayer-HEAD/libaf/control.h 2007-06-10 16:35:02.000000000 -0400
@@ -232,4 +232,7 @@
// Generic: respond to change in playback_speed
#define AF_CONTROL_PLAYBACK_SPEED 0x00002500 | AF_CONTROL_FILTER_SPECIFIC
+// ScaleTempo
+#define AF_CONTROL_SCALETEMPO_AMOUNT 0x00002600 | AF_CONTROL_FILTER_SPECIFIC
+
#endif /*__af_control_h */
More information about the MPlayer-dev-eng
mailing list