[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