[MPlayer-dev-eng] [PATCH] af_scale* command-line only

Robert Juliano juliano.1 at osu.edu
Wed Jun 13 06:50:47 CEST 2007


Thanks for the feedback Reimar and Diego.  I incorporated
most of your suggestions.

Here are just the filters (no interactivity and not tied to
playback_speed).  This patch only changes files in libaf and
then just enough to enable the filters.  This should be easier
to test and allow those in PAL-land to have the audio play
at a different tempo than the video (why they would want that,
I dunno know).

Usage:
  mplayer -af scaletempo=1.25 -speed 1.25 ...
  mplayer -af scalerate=.8 -speed=.8 ...
  mplayer -af scalepitch=1.5 ...

As for performance, chaining five instances of scaletempo on
my Athlon XP 3200, uses roughly 10% of the CPU.  The WSOLA
technique does little more then selectively skip forward/backward
overlapping each skip.  No DSP or FFT number crunching.  And
it produces quite decent results.

Enjoy.

Files:
A      libaf/af_scaletempo.c
A      libaf/af_scalerate.c
A      libaf/af_scalepitch.c
M      libaf/Makefile
M      libaf/control.h
M      libaf/af.c
M      libaf/af.h

Index: libaf/af_scalerate.c
===================================================================
--- libaf/af_scalerate.c	(revision 0)
+++ libaf/af_scalerate.c	(revision 0)
@@ -0,0 +1,131 @@
+/*
+ * scalerate audio filter
+ * (cc) GPL 2007 MPlayer / Robert Juliano
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer 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.
+ *
+ * MPlayer 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 MPlayer; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * scale rate (both time and tempo) (i.e. chipmunk effect)
+ *
+ * basic technique:
+ *   pretend input rate is scaled rate and resample to input rate
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "af.h"
+
+#ifdef USE_LIBAVCODEC
+  #define RESAMPLER "lavcresample"
+#else
+  #define RESAMPLER "resample"
+#endif
+
+// Data for specific instances of this filter
+typedef struct af_scalerate_s
+{
+  af_instance_t* resampler;
+  float scale;
+} af_scalerate_t;
+
+// Initialization and runtime control
+static int control(struct af_instance_s* af, int cmd, void* arg)
+{
+  af_scalerate_t* s = af->setup;
+  af_instance_t* resampler = s->resampler;
+  switch(cmd){
+  case AF_CONTROL_REINIT:{
+    af_data_t* c = (af_data_t*)arg;
+    int base_rate;
+    int rv;
+
+    base_rate = c->rate;
+    resampler->control(resampler, AF_CONTROL_RESAMPLE_RATE, &base_rate);
+    c->rate = base_rate * s->scale;
+    if (AF_OK != (rv = resampler->control(resampler, AF_CONTROL_REINIT, c)))
+      return rv;
+    c->rate = base_rate;
+    af->data = resampler->data;
+    af->mul.n = resampler->mul.n;
+    af->mul.d = resampler->mul.d;
+    af->delay = resampler->delay;
+
+    return af_test_output(af,(af_data_t*)arg);
+  }
+  case AF_CONTROL_SCALERATE_AMOUNT | AF_CONTROL_SET:
+    s->scale = *(float*)arg;
+    return AF_OK;
+  case AF_CONTROL_SCALERATE_AMOUNT | AF_CONTROL_GET:
+    *(float*)arg = s->scale;
+    return AF_OK;
+  case AF_CONTROL_COMMAND_LINE:{
+    float f;
+    sscanf((char*)arg,"%f", &f);
+    return control(af, AF_CONTROL_SCALERATE_AMOUNT | AF_CONTROL_SET, &f);
+  }
+  }
+  return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+  af_scalerate_t* s   = af->setup;
+  s->resampler->uninit(s->resampler);
+  free(s->resampler);
+  free(af->setup);
+}
+
+// Filter data through filter
+static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+{
+  af_scalerate_t* s = af->setup;
+  return s->resampler->play(s->resampler, data);
+}
+
+// Allocate memory and set function pointers
+static int af_open(af_instance_t* af){
+  af_scalerate_t* s;
+
+  af->control=control;
+  af->uninit=uninit;
+  af->play=play;
+  af->mul.d=1;
+  af->mul.n=1;
+  af->setup=calloc(1,sizeof(af_scalerate_t));
+  if(af->setup == NULL)
+    return AF_ERROR;
+
+  s = af->setup;
+  s->resampler = af_create(af->stream, RESAMPLER);
+  if (!s->resampler)
+    return AF_ERROR;
+
+  return AF_OK;
+}
+
+// Description of this filter
+af_info_t af_info_scalerate = {
+  "Scales audio rate (both pitch and tempo) (i.e. chipmunk effect)",
+  "scalerate",
+  "Robert Juliano",
+  "",
+  AF_FLAGS_REENTRANT,
+  af_open
+};
Index: libaf/control.h
===================================================================
--- libaf/control.h	(revision 23550)
+++ libaf/control.h	(working copy)
@@ -229,4 +229,9 @@
 #define AF_CONTROL_SS_FREQ		0x00002300 | AF_CONTROL_FILTER_SPECIFIC
 #define AF_CONTROL_SS_DECAY		0x00002400 | AF_CONTROL_FILTER_SPECIFIC
 
+// Percentage to scale tempo, rate, or pitch
+#define AF_CONTROL_SCALETEMPO_AMOUNT	0x00002500 | AF_CONTROL_FILTER_SPECIFIC
+#define AF_CONTROL_SCALERATE_AMOUNT	0x00002600 | AF_CONTROL_FILTER_SPECIFIC
+#define AF_CONTROL_SCALEPITCH_AMOUNT	0x00002700 | AF_CONTROL_FILTER_SPECIFIC
+
 #endif /*__af_control_h */
Index: libaf/Makefile
===================================================================
--- libaf/Makefile	(revision 23550)
+++ libaf/Makefile	(working copy)
@@ -16,6 +16,9 @@
               af_karaoke.c \
               af_pan.c \
               af_resample.c \
+              af_scalepitch.c \
+              af_scalerate.c \
+              af_scaletempo.c \
               af_sinesuppress.c \
               af_sub.c \
               af_surround.c \
Index: libaf/af_scaletempo.c
===================================================================
--- libaf/af_scaletempo.c	(revision 0)
+++ libaf/af_scaletempo.c	(revision 0)
@@ -0,0 +1,303 @@
+/*
+ * scaletempo audio filter
+ * (cc) GPL 2007 MPlayer / Robert Juliano
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer 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.
+ *
+ * MPlayer 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 MPlayer; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * scale tempo while maintaining pitch
+ * (WSOLA technique with cross correlation)
+ * inspired by SoundTouch library by Olli Parviainen
+ *
+ * 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
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "af.h"
+
+#define AOF(a, i) &(((float *)a)[i])
+
+// Data for specific instances of this filter
+typedef struct af_scaletempo_s
+{
+  float  scale;
+  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->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);
+    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->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->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;
+
+  // 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) {
+    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);
+    l->audio = realloc(l->audio, 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);
+    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);
+
+    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->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->scale;
+    s->ns_standing = s->ns_stride - s->np_overlap * nch;
+    s->ns_min_win = (s->np_seek + s->np_window) * nch;
+
+    af->mul.n = s->ns_stride;
+    af->mul.d = s->ns_stride_scaled;
+    af_frac_cancel(&af->mul);
+
+    s->buf_win = realloc(s->buf_win, s->ns_min_win * sizeof(float));
+    s->buf_ovl = realloc(s->buf_ovl, s->np_overlap * nch * sizeof(float));
+    s->buf_calc = realloc(s->buf_calc, s->np_overlap * nch * sizeof(float));
+    if(!s->buf_win || !s->buf_calc || !s->buf_ovl) {
+      af_msg(AF_MSG_FATAL, "[scaletempo] Out of memory\n");
+      return AF_ERROR;
+    }
+
+    return af_test_output(af,(af_data_t*)arg);
+  }
+  case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET:
+    s->scale = *(float*)arg;
+    return AF_OK;
+  case AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_GET:
+    *(float*)arg = s->scale;
+    return AF_OK;
+  case AF_CONTROL_COMMAND_LINE:{
+    float f;
+    sscanf((char*)arg,"%f", &f);
+    return control(af, AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET, &f);
+  }
+  }
+  return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+  af_scaletempo_t* s = af->setup;
+  free(af->data->audio);
+  free(af->data);
+  free(s->buf_win);
+  free(s->buf_ovl);
+  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->setup;
+  s->scale = 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_REENTRANT,
+  af_open
+};
Index: libaf/af_scalepitch.c
===================================================================
--- libaf/af_scalepitch.c	(revision 0)
+++ libaf/af_scalepitch.c	(revision 0)
@@ -0,0 +1,171 @@
+/*
+ * scalepitch audio filter
+ * (cc) GPL 2007 MPlayer / Robert Juliano
+ *
+ * This file is part of MPlayer.
+ *
+ * MPlayer 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.
+ *
+ * MPlayer 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 MPlayer; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * scale pitch without scaling tempo
+ *
+ * basic technique:
+ *   scale pitch and tempo with scalerate + unscale tempo with scaletempo
+ *
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "af.h"
+
+// Data for specific instances of this filter
+typedef struct af_scalepitch_s
+{
+  af_instance_t* scaler_rate;
+  af_instance_t* scaler_tempo;
+  af_instance_t* converter;
+  float  scale;
+} af_scalepitch_t;
+
+// Initialization and runtime control
+static int control(struct af_instance_s* af, int cmd, void* arg)
+{
+  af_scalepitch_t* s = af->setup;
+  af_instance_t* scaler_tempo  = s->scaler_tempo;
+  af_instance_t* scaler_rate  = s->scaler_rate;
+  af_instance_t* converter  = s->converter;
+  switch(cmd){
+  case AF_CONTROL_REINIT:{
+    af_data_t* c = (af_data_t*)arg;
+    af_data_t fmt_rate, fmtc, fmt_tempo;
+    float tempo, rate;
+    int rv;
+
+    if (s->scale == 1.0)
+      return AF_DETACH;
+
+    // init sub filters
+    tempo = 1.0 / s->scale;
+    rate = s->scale;
+    if (AF_OK != (rv = scaler_tempo->control(scaler_tempo, AF_CONTROL_SCALETEMPO_AMOUNT | AF_CONTROL_SET, &tempo)))
+      return rv;
+    if (AF_OK != (rv = scaler_rate->control(scaler_rate, AF_CONTROL_SCALERATE_AMOUNT | AF_CONTROL_SET, &rate)))
+      return rv;
+
+    memcpy(&fmt_rate, c, sizeof(af_data_t));
+    if (AF_ERROR == (rv = scaler_rate->control(scaler_rate, AF_CONTROL_REINIT, &fmt_rate)))
+      return rv;
+    memcpy(&fmt_tempo, &fmt_rate, sizeof(af_data_t));
+    if (AF_ERROR == (rv = scaler_tempo->control(scaler_tempo, AF_CONTROL_REINIT, &fmt_tempo)))
+      return rv;
+
+    // convert
+    if (scaler_rate->data->format != fmt_tempo.format) {
+      memcpy(&fmtc, scaler_rate->data, sizeof(af_data_t));
+      if (AF_ERROR == (rv = converter->control(converter, AF_CONTROL_FORMAT_FMT | AF_CONTROL_SET, &(fmt_tempo.format))) )
+	return rv;
+      if (AF_ERROR == (rv = converter->control(converter, AF_CONTROL_REINIT, &fmtc)) )
+      return rv;
+    } else {
+      free(s->converter);
+    }
+
+    af->data = scaler_tempo->data;
+    if (c->format  != fmt_rate.format
+        || c->bps  != fmt_rate.bps
+        || c->rate != fmt_rate.rate
+        || c->nch  != fmt_rate.nch) {
+      memcpy(c, &fmt_rate, sizeof(af_data_t));
+      return AF_FALSE;
+    }
+
+    return AF_OK;
+  }
+  case AF_CONTROL_SCALEPITCH_AMOUNT | AF_CONTROL_SET:
+    s->scale = *(float*)arg;
+    return AF_OK;
+  case AF_CONTROL_SCALEPITCH_AMOUNT | AF_CONTROL_GET:
+    *(float*)arg = s->scale;
+    return AF_OK;
+  case AF_CONTROL_COMMAND_LINE:{
+    float f;
+    sscanf((char*)arg,"%f", &f);
+    return control(af, AF_CONTROL_SCALEPITCH_AMOUNT | AF_CONTROL_SET, &f);
+  }
+  }
+  return AF_UNKNOWN;
+}
+
+// Deallocate memory
+static void uninit(struct af_instance_s* af)
+{
+  af_scalepitch_t* s = af->setup;
+  s->scaler_tempo->uninit(s->scaler_tempo);
+  free(s->scaler_tempo);
+  s->scaler_rate->uninit(s->scaler_rate);
+  free(s->scaler_rate);
+  if(s->converter) {
+    s->converter->uninit(s->converter);
+    free(s->converter);
+  }
+  free(af->setup);
+}
+
+// Filter data through filter
+static af_data_t* play(struct af_instance_s* af, af_data_t* data)
+{
+  af_scalepitch_t* s = af->setup;
+  af_data_t* l;
+  l = s->scaler_rate->play(s->scaler_rate, data);
+  if (s->converter)
+    s->converter->play(s->converter, l);
+  s->scaler_tempo->play(s->scaler_tempo, l);
+  return l;
+}
+
+// Allocate memory and set function pointers
+static int af_open(af_instance_t* af){
+  af_scalepitch_t* s;
+
+  af->control=control;
+  af->uninit=uninit;
+  af->play=play;
+  af->mul.d=1;
+  af->mul.n=1;
+  af->setup=calloc(1,sizeof(af_scalepitch_t));
+  if(af->setup == NULL)
+    return AF_ERROR;
+
+  s = af->setup;
+  s->scale = 1.0;
+  s->scaler_tempo = af_create(af->stream, "scaletempo");
+  s->scaler_rate = af_create(af->stream, "scalerate");
+  s->converter = af_create(af->stream, "format");
+  if (!s->scaler_tempo || !s->scaler_rate || !s->converter)
+    return AF_ERROR;
+
+  return AF_OK;
+}
+
+// Description of this filter
+af_info_t af_info_scalepitch = {
+  "Scale pitch without scaling tempo",
+  "scalepitch",
+  "Robert Juliano",
+  "",
+  AF_FLAGS_REENTRANT,
+  af_open
+};
Index: libaf/af.c
===================================================================
--- libaf/af.c	(revision 23550)
+++ libaf/af.c	(working copy)
@@ -31,6 +31,9 @@
 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;
+extern af_info_t af_info_scalerate;
+extern af_info_t af_info_scalepitch;
 
 static af_info_t* filter_list[]={ 
    &af_info_dummy,
@@ -61,6 +64,9 @@
    &af_info_center,
    &af_info_sinesuppress,
    &af_info_karaoke,
+   &af_info_scaletempo,
+   &af_info_scalerate,
+   &af_info_scalepitch,
    NULL 
 };
 
@@ -100,7 +106,7 @@
 
 /*/ Function for creating a new filter of type name. The name may
   contain the commandline parameters for the filter */
-static af_instance_t* af_create(af_stream_t* s, char* name)
+af_instance_t* af_create(af_stream_t* s, char* name)
 {
   char* cmdline = name;
 
@@ -131,6 +137,8 @@
   
   af_msg(AF_MSG_VERBOSE,"[libaf] Adding filter %s \n",name);
   
+  new->stream = s;
+
   // Initialize the new filter
   if(AF_OK == new->info->open(new) && 
      AF_ERROR < new->control(new,AF_CONTROL_POST_CREATE,&s->cfg)){
Index: libaf/af.h
===================================================================
--- libaf/af.h	(revision 23550)
+++ libaf/af.h	(working copy)
@@ -64,6 +64,7 @@
   af_data_t* data; // configuration for outgoing data stream
   struct af_instance_s* next;
   struct af_instance_s* prev;  
+  struct af_stream_s* stream; // allow filters to create, add, etc. other filters
   double delay; // Delay caused by the filter [ms]
   frac_t mul; /* length multiplier: how much does this instance change
 		 the length of the buffer. */
@@ -173,6 +174,16 @@
 void af_remove(af_stream_t* s, af_instance_t* af);
 
 /**
+ * \brief create instance of filter
+ * \param name name of filter to create with command line paramters
+ * \return pointer to the new filter, NULL if failed
+ *
+ * Does not add filter to filter chain.  Useful for swallowing other
+ * filters.  Use af_add to add filter to filter chain.
+ */
+af_instance_t* af_create(af_stream_t* s, char* name);
+
+/**
  * \brief find filter in chain by name
  * \param name name of the filter to find
  * \return first filter with right name or NULL if not found



More information about the MPlayer-dev-eng mailing list