shithub: sox

Download patch

ref: f9dc9dd5e2523833fc771b8b6f748fc4d95c2f06
parent: 9d11b22fadb9698e745947151f175c472a62aac4
author: robs <robs>
date: Sat Oct 18 08:39:22 EDT 2008

reprieve for stretch

--- a/ChangeLog
+++ b/ChangeLog
@@ -4,7 +4,7 @@
 This file contains a list of all changes starting after the release of
 sox-11gamma.
 
-sox-14.1.1	2008-TBD
+sox-14.2.0	2008-TBD
 ----------
 
 Previously deprecated features that have been removed in this release:
@@ -12,7 +12,6 @@
   Deprec-  Feature    [O(ption)]
   ated in  [F(ormat)] [E(ffect)]   Replacement
   -------  ----------------------  ----------------------
-  14.0.0   E stretch               ~= tempo
   14.0.0   E pitch                 new pitch = old key
 
 Previously deprecated features (to be removed in future):
@@ -20,10 +19,11 @@
   Deprec-  Feature    [O(ption)]                           Removal
   ated in  [F(ormat)] [E(ffect)]   Replacement             due after
   -------  ----------------------  ----------------------  -------
-  14.1.0   F flac: libFLAC 1.1.1   libFLAC > 1.1.1         2009-01-29
+  14.0.0   E stretch               ~= tempo                14.2.0 + 6 months
   14.1.0   E resample              ~= rate                 2009-01-29
   14.1.0   E polyphase             ~= rate                 2009-01-29
   14.1.0   E rabbit                ~= rate                 2009-01-29
+  14.1.0   F flac: libFLAC 1.1.1   libFLAC > 1.1.1         2009-01-29
   14.1.0   F wve (native)          wve (libsndfile)        2009-07-29
   14.1.0   Behaviour whereby       soxi                    2009-07-29
            sox -V file(s) -n
@@ -34,8 +34,8 @@
   Deprec-  Feature    [O(ption)]                           Removal
   ated in  [F(ormat)] [E(ffect)]   Replacement             due after
   -------  ----------------------  ----------------------  -------
-  14.1.1   E key                   renamed to pitch        14.1.1 + 6 months
-  14.1.1   E pan                   ~= mixer/remix          14.1.1 + 6 months
+  14.2.0   E key                   renamed to pitch        14.2.0 + 6 months
+  14.2.0   E pan                   ~= mixer/remix          14.2.0 + 6 months
 
 File formats:
 
@@ -55,6 +55,7 @@
   o New `bend' effect; pitch bending.  (robs)
   o New -b option for the norm effect; can be used to fix stereo
     imbalance.  (robs)
+  o Wider tempo range for `tempo' effect.  (robs)
   o New --effects-file option to read effects and arguments from
     a file instead of command line. (cbagwell)
   o `filter' effect now supports --plot.  (robs)
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,7 +13,7 @@
   else(${ver} LESS 403)
     add_definitions(-Wtraditional-conversion)
   endif(${ver} LESS 403)
-  add_definitions(-Werror)
+  #add_definitions(-Werror)
 endif(CMAKE_COMPILER_IS_GNUCC)
 
 if (NOT EXTERNAL_GSM)
@@ -36,7 +36,7 @@
   compandt                        output          reverb          tremolo
   contrast        filter          pad             reverse         trim
   dcshift         flanger         pan             silence         vol
-  delay           input           phaser          skeleff
+  delay           input           phaser          skeleff stretch
   dither          loudness        pitch           speed
 )
 set(formats_srcs
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -262,7 +262,7 @@
 	pan.c phaser.c pitch.c polyphas.c rabbit.c rate.c \
 	rate_filters.h rate_half_fir.h rate_poly_fir0.h rate_poly_fir.h \
 	remix.c repeat.c resample.c reverb.c reverse.c silence.c skeleff.c \
-	speed.c	splice.c stat.c swap.c synth.c tempo.c tremolo.c \
+	speed.c	splice.c stat.c swap.c stretch.c synth.c tempo.c tremolo.c \
 	trim.c vol.c
 if HAVE_PNG
     libsfx_la_SOURCES += spectrogram.c
--- a/src/effects.h
+++ b/src/effects.h
@@ -73,6 +73,7 @@
   EFFECT(speed)
   EFFECT(splice)
   EFFECT(stat)
+  EFFECT(stretch)
   EFFECT(swap)
   EFFECT(synth)
   EFFECT(tempo)
--- /dev/null
+++ b/src/stretch.c
@@ -1,0 +1,324 @@
+/* libSoX Basic time stretcher.
+ * (c) march/april 2000 Fabien COELHO <fabien@coelho.net> for sox.
+ *
+ * cross fade samples so as to go slower or faster.
+ *
+ * The filter is based on 6 parameters:
+ * - stretch factor f
+ * - window size w
+ * - input step i
+ *   output step o=f*i
+ * - steady state of window s, ss = s*w
+ *
+ * I decided of the default values of these parameters based
+ * on some small non extensive tests. maybe better defaults
+ * can be suggested.
+ */
+#include "sox_i.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#define DEFAULT_SLOW_SHIFT_RATIO        0.8
+#define DEFAULT_FAST_SHIFT_RATIO        1.0
+
+#define DEFAULT_STRETCH_WINDOW          20.0  /* ms */
+
+typedef enum { input_state, output_state } stretch_status_t;
+
+typedef struct {
+  /* options
+   * FIXME: maybe shift could be allowed > 1.0 with factor < 1.0 ???
+   */
+  double factor;   /* strech factor. 1.0 means copy. */
+  double window;   /* window in ms */
+  double shift;    /* shift ratio wrt window. <1.0 */
+  double fading;   /* fading ratio wrt window. <0.5 */
+
+  /* internal stuff */
+  stretch_status_t state; /* automaton status */
+
+  size_t segment;         /* buffer size */
+  size_t index;        /* next available element */
+  sox_sample_t *ibuf;      /* input buffer */
+  size_t ishift;       /* input shift */
+
+  size_t oindex;       /* next evailable element */
+  double * obuf;   /* output buffer */
+  size_t oshift;       /* output shift */
+
+  size_t overlap;        /* fading size */
+  double * fade_coefs;   /* fading, 1.0 -> 0.0 */
+
+} priv_t;
+
+/*
+ * Process options
+ */
+static int getopts(sox_effect_t * effp, int n, char **argv)
+{
+  priv_t * p = (priv_t *) effp->priv;
+
+  /* default options */
+  p->factor = 1.0; /* default is no change */
+  p->window = DEFAULT_STRETCH_WINDOW;
+
+  if (n > 0 && !sscanf(argv[0], "%lf", &p->factor)) {
+    sox_fail("error while parsing factor");
+    return lsx_usage(effp);
+  }
+
+  if (n > 1 && !sscanf(argv[1], "%lf", &p->window)) {
+    sox_fail("error while parsing window size");
+    return lsx_usage(effp);
+  }
+
+  if (n > 2) {
+    switch (argv[2][0]) {
+    case 'l':
+    case 'L':
+      break;
+    default:
+      sox_fail("error while parsing fade type");
+      return lsx_usage(effp);
+    }
+  }
+
+  /* default shift depends whether we go slower or faster */
+  p->shift = (p->factor <= 1.0) ?
+    DEFAULT_FAST_SHIFT_RATIO: DEFAULT_SLOW_SHIFT_RATIO;
+
+  if (n > 3 && !sscanf(argv[3], "%lf", &p->shift)) {
+    sox_fail("error while parsing shift ratio");
+    return lsx_usage(effp);
+  }
+
+  if (p->shift > 1.0 || p->shift <= 0.0) {
+    sox_fail("error with shift ratio value");
+    return lsx_usage(effp);
+  }
+
+  /* default fading stuff...
+     it makes sense for factor >= 0.5 */
+  if (p->factor < 1.0)
+    p->fading = 1.0 - (p->factor * p->shift);
+  else
+    p->fading = 1.0 - p->shift;
+  if (p->fading > 0.5)
+    p->fading = 0.5;
+
+  if (n > 4 && !sscanf(argv[4], "%lf", &p->fading)) {
+    sox_fail("error while parsing fading ratio");
+    return lsx_usage(effp);
+  }
+
+  if (p->fading > 0.5 || p->fading < 0.0) {
+    sox_fail("error with fading ratio value");
+    return lsx_usage(effp);
+  }
+
+  return SOX_SUCCESS;
+}
+
+/*
+ * Start processing
+ */
+static int start(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  size_t i;
+
+  if (p->factor == 1)
+    return SOX_EFF_NULL;
+
+  p->state = input_state;
+
+  p->segment = (int)(effp->out_signal.rate * 0.001 * p->window);
+  /* start in the middle of an input to avoid initial fading... */
+  p->index = p->segment / 2;
+  p->ibuf = lsx_malloc(p->segment * sizeof(sox_sample_t));
+
+  /* the shift ratio deal with the longest of ishift/oshift
+     hence ishift<=segment and oshift<=segment. */
+  if (p->factor < 1.0) {
+    p->ishift = p->shift * p->segment;
+    p->oshift = p->factor * p->ishift;
+  } else {
+    p->oshift = p->shift * p->segment;
+    p->ishift = p->oshift / p->factor;
+  }
+  assert(p->ishift <= p->segment);
+  assert(p->oshift <= p->segment);
+
+  p->oindex = p->index; /* start as synchronized */
+  p->obuf = lsx_malloc(p->segment * sizeof(double));
+  p->overlap = (int)(p->fading * p->segment);
+  p->fade_coefs = lsx_malloc(p->overlap * sizeof(double));
+
+  /* initialize buffers */
+  for (i = 0; i<p->segment; i++)
+    p->ibuf[i] = 0;
+
+  for (i = 0; i<p->segment; i++)
+    p->obuf[i] = 0.0;
+
+  if (p->overlap>1) {
+    double slope = 1.0 / (p->overlap - 1);
+    p->fade_coefs[0] = 1.0;
+    for (i = 1; i < p->overlap - 1; i++)
+      p->fade_coefs[i] = slope * (p->overlap - i - 1);
+    p->fade_coefs[p->overlap - 1] = 0.0;
+  } else if (p->overlap == 1)
+    p->fade_coefs[0] = 1.0;
+
+  sox_debug("start: (factor=%g segment=%g shift=%g overlap=%g)\nstate=%d\n"
+      "segment=%lu\nindex=%lu\nishift=%lu\noindex=%lu\noshift=%lu\noverlap=%lu",
+      p->factor, p->window, p->shift, p->fading, p->state,
+      (unsigned long)p->segment, (unsigned long)p->index,
+      (unsigned long)p->ishift, (unsigned long)p->oindex,
+      (unsigned long)p->oshift, (unsigned long)p->overlap);
+
+  return SOX_SUCCESS;
+}
+
+/* accumulates input ibuf to output obuf with fading fade_coefs */
+static void combine(priv_t * p)
+{
+  size_t i;
+
+  /* fade in */
+  for (i = 0; i < p->overlap; i++)
+    p->obuf[i] += p->fade_coefs[p->overlap - 1 - i] * p->ibuf[i];
+
+  /* steady state */
+  for (; i < p->segment - p->overlap; i++)
+    p->obuf[i] += p->ibuf[i];
+
+  /* fade out */
+  for (; i<p->segment; i++)
+    p->obuf[i] += p->fade_coefs[i - p->segment + p->overlap] * p->ibuf[i];
+}
+
+/*
+ * Processes flow.
+ */
+static int flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
+                    size_t *isamp, size_t *osamp)
+{
+  priv_t * p = (priv_t *) effp->priv;
+  size_t iindex = 0, oindex = 0;
+  size_t i;
+
+  while (iindex<*isamp && oindex<*osamp) {
+    if (p->state == input_state) {
+      size_t tocopy = min(*isamp-iindex,
+                             p->segment-p->index);
+
+      memcpy(p->ibuf + p->index, ibuf + iindex, tocopy * sizeof(sox_sample_t));
+
+      iindex += tocopy;
+      p->index += tocopy;
+
+      if (p->index == p->segment) {
+        /* compute */
+        combine(p);
+
+        /* shift input */
+        for (i = 0; i + p->ishift < p->segment; i++)
+          p->ibuf[i] = p->ibuf[i+p->ishift];
+
+        p->index -= p->ishift;
+
+        /* switch to output state */
+        p->state = output_state;
+      }
+    }
+
+    if (p->state == output_state) {
+      while (p->oindex < p->oshift && oindex < *osamp) {
+        float f;
+        f = p->obuf[p->oindex++];
+        SOX_SAMPLE_CLIP_COUNT(f, effp->clips);
+        obuf[oindex++] = f;
+      }
+
+      if (p->oindex >= p->oshift && oindex<*osamp) {
+        p->oindex -= p->oshift;
+
+        /* shift internal output buffer */
+        for (i = 0; i + p->oshift < p->segment; i++)
+          p->obuf[i] = p->obuf[i + p->oshift];
+
+        /* pad with 0 */
+        for (; i < p->segment; i++)
+          p->obuf[i] = 0.0;
+
+        p->state = input_state;
+      }
+    }
+  }
+
+  *isamp = iindex;
+  *osamp = oindex;
+
+  return SOX_SUCCESS;
+}
+
+
+/*
+ * Drain buffer at the end
+ * maybe not correct ? end might be artificially faded?
+ */
+static int drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp)
+{
+  priv_t * p = (priv_t *) effp->priv;
+  size_t i;
+  size_t oindex = 0;
+
+  if (p->state == input_state) {
+    for (i=p->index; i<p->segment; i++)
+      p->ibuf[i] = 0;
+
+    combine(p);
+
+    p->state = output_state;
+  }
+
+  while (oindex<*osamp && p->oindex<p->index) {
+    float f = p->obuf[p->oindex++];
+    SOX_SAMPLE_CLIP_COUNT(f, effp->clips);
+    obuf[oindex++] = f;
+  }
+
+  *osamp = oindex;
+
+  if (p->oindex == p->index)
+    return SOX_EOF;
+  else
+    return SOX_SUCCESS;
+}
+
+
+static int stop(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *) effp->priv;
+
+  free(p->ibuf);
+  free(p->obuf);
+  free(p->fade_coefs);
+  return SOX_SUCCESS;
+}
+
+const sox_effect_handler_t *sox_stretch_effect_fn(void)
+{
+  static const sox_effect_handler_t handler = {
+    "stretch",
+    "factor [window fade shift fading]\n"
+    "       (expansion, frame in ms, lin/..., unit<1.0, unit<0.5)\n"
+    "       (defaults: 1.0 20 lin ...)",
+    SOX_EFF_LENGTH | SOX_EFF_DEPRECATED,
+    getopts, start, flow, drain, stop, NULL, sizeof(priv_t)
+  };
+  return &handler;
+}