shithub: sox

Download patch

ref: bfbd4adf5cb81d037eef717b6a199c554748bca4
parent: 0adab62cc4a342a0f0b30d2934e33abcb6586cd4
author: robs <robs>
date: Sat Jun 2 13:00:25 EDT 2007

Added effects-chain buffering for effects that use a window

--- a/AUTHORS
+++ b/AUTHORS
@@ -17,7 +17,7 @@
                 formats.
 		Effects: pad, bass, treble, new flanger, soft-knee companding,
                 speed via resampling, filters makeover inc. gnuplot & octave
-                plotting.
+                plotting; new effects chain with buffering and any # channels.
                 Others: open input files via URL, file merging, play
                 multiple files with mixed format types, play with replay-gain,
                 building with cmake, much manual improvement and expansion,
--- a/ChangeLog
+++ b/ChangeLog
@@ -67,7 +67,11 @@
   o Got rid of several hundred compiler warnings.  (robs, Reuben Thomas)
   o Added basic performance testing of I/O.  (Reuben Thomas)
   o Effects chain processing now available in libSoX.  (robs)
+  o Added effects-chain buffering for effects that use a window [FR#
+    1621695].  (robs)
   o Added cmake build files for Win32.  (robs)
+
+
 
 sox-13.0.0
 ----------
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -95,7 +95,116 @@
   DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/soxstdint.h.cmake
   )
 
-add_library(lib${PROJECT_NAME} 8svx adpcm adpcms aifc-fmt aiff aiff-fmt al-fmt au auto avr biquad biquads cdr chorus compand compandt cvsd cvsd-fmt dat dcshift dither dvms-fmt earwax echo echos effects fade FFT filter flanger g711 g721 g723_24 g723_40 g72x getopt1 getopt gsm handlers hcom ima-fmt ima_rw la-fmt lpc10 lu-fmt maud mcompand misc mixer noiseprof noisered nulfile pad pan phaser pitch polyphas prc rate raw raw-fmt repeat resample reverb reverse s1-fmt s2-fmt s3-fmt s4-fmt sf silence skeleff skelform smp sndrtool soxio speed sphere stat stretch swap synth tremolo trim tx16w u1-fmt u2-fmt u3-fmt u4-fmt ul-fmt util vibro voc vol vox vox-fmt wav wve xa xmalloc soxstdint ${optional_srcs})
+set(effects_srcs
+  biquad
+  biquads
+  chorus
+  compand
+  compandt
+  dcshift
+  dither
+  earwax
+  echo
+  echos
+  effects
+  fade
+  FFT
+  filter
+  flanger
+  mcompand
+  mixer
+  noiseprof
+  noisered
+  pad
+  pan
+  phaser
+  pitch
+  polyphas
+  rate
+  repeat
+  resample
+  reverb
+  reverse
+  silence
+  skeleff
+  speed
+  stat
+  stretch
+  swap
+  synth
+  tremolo
+  trim
+  vibro
+  vol
+)
+set(formats_srcs
+  8svx
+  adpcm
+  adpcms
+  aifc-fmt
+  aiff
+  aiff-fmt
+  al-fmt
+  au
+  auto
+  avr
+  cdr
+  cvsd
+  cvsd-fmt
+  dat
+  dvms-fmt
+  formats
+  g711
+  g721
+  g723_24
+  g723_40
+  g72x
+  gsm
+  hcom
+  ima-fmt
+  ima_rw
+  la-fmt
+  lpc10
+  lu-fmt
+  maud
+  nulfile
+  prc
+  raw
+  raw-fmt
+  s1-fmt
+  s2-fmt
+  s3-fmt
+  s4-fmt
+  sf
+  skelform
+  smp
+  sndrtool
+  sphere
+  tx16w
+  u1-fmt
+  u2-fmt
+  u3-fmt
+  u4-fmt
+  ul-fmt
+  voc
+  vox
+  vox-fmt
+  wav
+  wve
+  xa
+)
+add_library(lib${PROJECT_NAME}
+  getopt
+  getopt1
+  misc
+  soxio
+  soxstdint
+  util
+  xmalloc
+  ${effects_srcs}
+  ${formats_srcs}
+  ${optional_srcs}
+)
 add_executable(${PROJECT_NAME} ${PROJECT_NAME}.c)
 target_link_libraries(${PROJECT_NAME} lib${PROJECT_NAME} lpc10 ${optional_libs})
 add_executable(sox_sample_test sox_sample_test.c)
--- a/src/effects.c
+++ b/src/effects.c
@@ -22,6 +22,13 @@
 #include <string.h>
 #include <strings.h>
 
+#undef sox_fail
+#undef sox_warn
+#undef sox_report
+#define sox_fail sox_message_filename=effp->handler.name,sox_fail
+#define sox_warn sox_message_filename=effp->handler.name,sox_warn
+#define sox_report sox_message_filename=effp->handler.name,sox_report
+
 /* dummy effect routine for do-nothing functions */
 static int effect_nothing(sox_effect_t * effp UNUSED)
 {
@@ -32,7 +39,7 @@
 {
   /* Pass through samples verbatim */
   *isamp = *osamp = min(*isamp, *osamp);
-  memcpy(obuf, ibuf, *isamp * sizeof(sox_ssample_t));
+  memcpy(obuf, ibuf, *isamp * sizeof(*obuf));
   return SOX_SUCCESS;
 }
 
@@ -43,16 +50,13 @@
   return SOX_EOF;
 }
 
-static int effect_nothing_getopts(sox_effect_t * effp, int n, char **argv UNUSED)
+static int effect_nothing_getopts(sox_effect_t * effp, int argc, char **argv UNUSED)
 {
-#undef sox_fail
-#define sox_fail sox_message_filename=effp->handler.name,sox_fail
-  if (n) {
+  if (argc) {
     sox_fail(effp->handler.usage);
-    return (SOX_EOF);
+    return SOX_EOF;
   }
-  return (SOX_SUCCESS);
-#undef sox_fail
+  return SOX_SUCCESS;
 }
 
 
@@ -60,28 +64,28 @@
 
 sox_effect_handler_t const * sox_find_effect(char const * name)
 {
-  int i;
+  int e;
 
-  for (i = 0; sox_effect_fns[i]; ++i) {
-    const sox_effect_handler_t *e = sox_effect_fns[i] ();
-    if (e && e->name && strcasecmp(e->name, name) == 0)
-      return e;                 /* Found it. */
+  for (e = 0; sox_effect_fns[e]; ++e) {
+    const sox_effect_handler_t *effp = sox_effect_fns[e] ();
+    if (effp && effp->name && strcasecmp(effp->name, name) == 0)
+      return effp;                 /* Found it. */
   }
   return NULL;
 }
 
-void sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const * e)
+void sox_create_effect(sox_effect_t * effp, sox_effect_handler_t const * eh)
 {
-  assert(e);
+  assert(eh);
   memset(effp, 0, sizeof(*effp));
   effp->global_info = &effects_global_info;
-  effp->handler = *e;
+  effp->handler = *eh;
   if (!effp->handler.getopts) effp->handler.getopts = effect_nothing_getopts;
-  if (!effp->handler.start) effp->handler.start = effect_nothing;
-  if (!effp->handler.flow) effp->handler.flow = effect_nothing_flow;
-  if (!effp->handler.drain) effp->handler.drain = effect_nothing_drain;
-  if (!effp->handler.stop) effp->handler.stop = effect_nothing;
-  if (!effp->handler.kill) effp->handler.kill = effect_nothing;
+  if (!effp->handler.start  ) effp->handler.start   = effect_nothing;
+  if (!effp->handler.flow   ) effp->handler.flow    = effect_nothing_flow;
+  if (!effp->handler.drain  ) effp->handler.drain   = effect_nothing_drain;
+  if (!effp->handler.stop   ) effp->handler.stop    = effect_nothing;
+  if (!effp->handler.kill   ) effp->handler.kill    = effect_nothing;
 }
 
 /*
@@ -96,66 +100,74 @@
  * calls.
  */
 
-int sox_update_effect(sox_effect_t * effp, const sox_signalinfo_t *in, const sox_signalinfo_t *out, 
-                    int effect_mask)
+int sox_update_effect(sox_effect_t * effp, const sox_signalinfo_t * in,
+                      const sox_signalinfo_t * out, int effect_mask)
 {
-    effp->ininfo = *in;
-    effp->outinfo = *out;
+  effp->ininfo = *in;
+  effp->outinfo = *out;
 
-    if (in->channels != out->channels) {
-      /* Only effects with SOX_EFF_CHAN flag can actually handle
-       * outputing a different number of channels then the input.
-       */
-      if (!(effp->handler.flags & SOX_EFF_CHAN)) {
-        /* If this effect is being run before a SOX_EFF_CHAN effect
-         * then its output is the same as the input file; otherwise,
-         * its input contains the same number of channels as the
-         * output file. */
-        if (effect_mask & SOX_EFF_CHAN)
-          effp->ininfo.channels = out->channels;
-        else
-          effp->outinfo.channels = in->channels;
-      }
+  if (in->channels != out->channels) {
+    /* Only effects with SOX_EFF_CHAN flag can actually handle
+     * outputing a different number of channels then the input.
+     */
+    if (!(effp->handler.flags & SOX_EFF_CHAN)) {
+      /* If this effect is being run before a SOX_EFF_CHAN effect
+       * then its output is the same as the input file; otherwise,
+       * its input contains the same number of channels as the
+       * output file. */
+      if (effect_mask & SOX_EFF_CHAN)
+        effp->ininfo.channels = out->channels;
+      else
+        effp->outinfo.channels = in->channels;
     }
+  }
 
-    if (in->rate != out->rate)
-    {
-        /* Only SOX_EFF_RATE effects can handle an input that
-         * has a different sample rate from the output. */
-        if (!(effp->handler.flags & SOX_EFF_RATE))
-        {
-            if (effect_mask & SOX_EFF_RATE)
-                effp->ininfo.rate = out->rate;
-            else
-                effp->outinfo.rate = in->rate;
-        }
+  if (in->rate != out->rate) {
+    /* Only SOX_EFF_RATE effects can handle an input that
+     * has a different sample rate from the output. */
+    if (!(effp->handler.flags & SOX_EFF_RATE)) {
+      if (effect_mask & SOX_EFF_RATE)
+        effp->ininfo.rate = out->rate;
+      else
+        effp->outinfo.rate = in->rate;
     }
+  }
 
-    if (effp->handler.flags & SOX_EFF_CHAN)
-        effect_mask |= SOX_EFF_CHAN;
-    if (effp->handler.flags & SOX_EFF_RATE)
-        effect_mask |= SOX_EFF_RATE;
+  if (effp->handler.flags & SOX_EFF_CHAN)
+    effect_mask |= SOX_EFF_CHAN;
+  if (effp->handler.flags & SOX_EFF_RATE)
+    effect_mask |= SOX_EFF_RATE;
 
-    return effect_mask;
+  return effect_mask;
 }
 
-
 sox_effect_t * sox_effects[SOX_MAX_EFFECTS];
 unsigned sox_neffects;
 
-int sox_add_effect(sox_effect_t * e, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask)
+int sox_effect_set_imin(sox_effect_t * effp, sox_size_t imin)
 {
+  if (imin > sox_bufsiz / effp->flows) {
+    sox_fail("sox_bufsiz not big enough");
+    return SOX_EOF;
+  }
+
+  effp->imin = imin;
+  return SOX_SUCCESS;
+}
+
+int sox_add_effect(sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask)
+{
   unsigned f, flows;
 
   if (sox_neffects == SOX_MAX_EFFECTS)
     return SOX_EOF;
 
-  *effects_mask = sox_update_effect(e, in, out, *effects_mask);
+  *effects_mask = sox_update_effect(effp, in, out, *effects_mask);
 
-  flows = (e->handler.flags & SOX_EFF_MCHAN)? 1 : e->ininfo.channels;
+  flows = (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->ininfo.channels;
 
   sox_effects[sox_neffects] = xcalloc(flows, sizeof(sox_effects[sox_neffects][0]));
-  sox_effects[sox_neffects][0] = *e;
+  sox_effects[sox_neffects][0] = *effp;
   sox_effects[sox_neffects][0].flows = flows;
 
   for (f = 1; f < flows; ++f)
@@ -167,16 +179,17 @@
 
 static void stop_effect(unsigned e)
 {
-  unsigned i;
+  sox_effect_t * effp = &sox_effects[e][0];
+  unsigned f;
 
   sox_size_t clips = 0;
 
-  for (i = 0; i < sox_effects[e][0].flows; ++i) {
-    sox_effects[e][0].handler.stop(&sox_effects[e][i]);
-    clips += sox_effects[e][i].clips;
+  for (f = 0; f < effp->flows; ++f) {
+    effp->handler.stop(&sox_effects[e][f]);
+    clips += sox_effects[e][f].clips;
   }
   if (clips != 0)
-    sox_warn("'%s' clipped %u samples; decrease volume?",sox_effects[e][0].handler.name,clips);
+    sox_warn("clipped %u samples; decrease volume?", clips);
 }
 
 void sox_stop_effects(void)
@@ -186,56 +199,46 @@
     stop_effect(e);
 }
 
-static void kill_effect(unsigned e)
-{
-  sox_effects[e][0].handler.kill(&sox_effects[e][0]);/* One kill for all flows */
-}
-
-void sox_kill_effects(void)
-{
-  unsigned e;
-  for (e = 0; e < sox_neffects; ++e)
-    kill_effect(e);
-}
-
 int sox_start_effects(void)
 {
-  unsigned i, j;
+  unsigned e, f, i;
   int ret = SOX_SUCCESS;
 
-  for (i = 0; i < sox_neffects; ++i) {
-    sox_effect_t * e = &sox_effects[i][0];
-    sox_bool is_always_null = (e->handler.flags & SOX_EFF_NULL) != 0;
-    int (*start)(sox_effect_t * effp) = e->handler.start;
+  for (e = 0; e < sox_neffects; ++e) {
+    sox_effect_t * effp = &sox_effects[e][0];
+    sox_bool is_always_null = (effp->handler.flags & SOX_EFF_NULL) != 0;
+    int (*start)(sox_effect_t * effp) = effp->handler.start;
 
     if (is_always_null)
-      sox_report("'%s' has no effect (is a proxy effect)", e->handler.name);
+      sox_report("has no effect (is a proxy effect)");
     else {
-      e->clips = 0;
-      ret = start(e);
+      effp->clips = 0;
+      effp->imin = 0;
+      ret = start(effp);
       if (ret == SOX_EFF_NULL)
-        sox_warn("'%s' has no effect in this configuration", e->handler.name);
+        sox_report("has no effect in this configuration");
       else if (ret != SOX_SUCCESS)
         return SOX_EOF;
     }
     if (is_always_null || ret == SOX_EFF_NULL) { /* remove from the chain */
-      kill_effect(i);
-      free(sox_effects[i]);
+      free(sox_effects[e]);
       --sox_neffects;
-      for (j = i--; j < sox_neffects; ++j)
-        sox_effects[j] = sox_effects[j + 1];
+      for (i = e--; i < sox_neffects; ++i)
+        sox_effects[i] = sox_effects[i + 1];
     }
-    else for (j = 1; j < sox_effects[i][0].flows; ++j) {
-      sox_effects[i][j].clips = 0;
-      if (start(&sox_effects[i][j]) != SOX_SUCCESS)
+    else for (f = 1; f < sox_effects[e][0].flows; ++f) {
+      sox_effects[e][f].clips = 0;
+      if (start(&sox_effects[e][f]) != SOX_SUCCESS)
         return SOX_EOF;
     }
   }
-  for (i = 0; i < sox_neffects; ++i) {
-    sox_effect_t * e = &sox_effects[i][0];
-    sox_report("Effects chain: %-10s %uHz %u channels %s",
-        e->handler.name, e->ininfo.rate, e->ininfo.channels,
-        (e->handler.flags & SOX_EFF_MCHAN)? "(multi)" : "");
+  for (e = 0; e < sox_neffects; ++e) {
+    sox_effect_t * effp = &sox_effects[e][0];
+    #undef sox_report
+    #define sox_report     sox_message_filename="effects chain",sox_report
+    sox_report("%-10s %uHz %u channels %s",
+        effp->handler.name, effp->ininfo.rate, effp->ininfo.channels,
+        (effp->handler.flags & SOX_EFF_MCHAN)? "(multi)" : "");
   }
   return SOX_SUCCESS;
 }
@@ -242,34 +245,35 @@
 
 static sox_ssample_t **ibufc, **obufc; /* Channel interleave buffers */
 
-static int flow_effect(unsigned e)
+static int flow_effect(unsigned n)
 {
+  sox_effect_t * effp1 = &sox_effects[n - 1][0];
+  sox_effect_t * effp = &sox_effects[n][0];
   int effstatus = SOX_SUCCESS;
   sox_size_t i, f;
   const sox_ssample_t *ibuf;
-  sox_size_t idone = sox_effects[e - 1][0].olen - sox_effects[e - 1][0].odone;
-  sox_size_t odone = sox_bufsiz - sox_effects[e][0].olen;
+  sox_size_t idone = effp1->olen - effp1->odone;
+  sox_size_t odone = sox_bufsiz - effp->olen;
 
-  if (sox_effects[e][0].flows == 1)   /* Run effect on all channels at once */
-    effstatus = sox_effects[e][0].handler.flow(&sox_effects[e][0],
-      &sox_effects[e - 1][0].obuf[sox_effects[e - 1][0].odone],
-      &sox_effects[e][0].obuf[sox_effects[e][0].olen], &idone, &odone);
+  if (effp->flows == 1)   /* Run effect on all channels at once */
+    effstatus = effp->handler.flow(effp,
+        &effp1->obuf[effp1->odone], &effp->obuf[effp->olen], &idone, &odone);
   else {                         /* Run effect on each channel individually */
-    sox_ssample_t *obuf = &sox_effects[e][0].obuf[sox_effects[e][0].olen];
+    sox_ssample_t *obuf = &effp->obuf[effp->olen];
     sox_size_t idone_last, odone_last;
 
-    ibuf = &sox_effects[e - 1][0].obuf[sox_effects[e - 1][0].odone];
-    for (i = 0; i < idone; i += sox_effects[e][0].flows)
-      for (f = 0; f < sox_effects[e][0].flows; ++f)
-        ibufc[f][i / sox_effects[e][0].flows] = *ibuf++;
+    ibuf = &effp1->obuf[effp1->odone];
+    for (i = 0; i < idone; i += effp->flows)
+      for (f = 0; f < effp->flows; ++f)
+        ibufc[f][i / effp->flows] = *ibuf++;
 
-    for (f = 0; f < sox_effects[e][0].flows; ++f) {
-      sox_size_t idonec = idone / sox_effects[e][0].flows;
-      sox_size_t odonec = odone / sox_effects[e][0].flows;
-      int eff_status_c = sox_effects[e][0].handler.flow(&sox_effects[e][f],
+    for (f = 0; f < effp->flows; ++f) {
+      sox_size_t idonec = idone / effp->flows;
+      sox_size_t odonec = odone / effp->flows;
+      int eff_status_c = effp->handler.flow(&sox_effects[n][f],
           ibufc[f], obufc[f], &idonec, &odonec);
       if (f && (idonec != idone_last || odonec != odone_last)) {
-        sox_fail("'%s' flowed asymmetrically!", sox_effects[e][0].handler.name);
+        sox_fail("flowed asymmetrically!");
         effstatus = SOX_EOF;
       }
       idone_last = idonec;
@@ -280,41 +284,45 @@
     }
 
     for (i = 0; i < odone_last; ++i)
-      for (f = 0; f < sox_effects[e][0].flows; ++f)
+      for (f = 0; f < effp->flows; ++f)
         *obuf++ = obufc[f][i];
 
     idone = f * idone_last;
     odone = f * odone_last;
   }
-  sox_effects[e - 1][0].odone += idone;
-  if (sox_effects[e - 1][0].odone == sox_effects[e - 1][0].olen) /* Can reuse this buffer? */
-    sox_effects[e - 1][0].odone = sox_effects[e - 1][0].olen = 0;
+  effp1->odone += idone;
+  if (effp1->odone == effp1->olen)
+    effp1->odone = effp1->olen = 0;
+  else if (effp1->olen - effp1->odone < effp->imin ) { /* Need to refill? */
+    memmove(effp1->obuf, &effp1->obuf[effp1->odone], (effp1->olen - effp1->odone) * sizeof(*effp1->obuf));
+    effp1->olen -= effp1->odone;
+    effp1->odone = 0;
+  }
 
-  sox_effects[e][0].olen += odone;
+  effp->olen += odone;
 
   return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
 }
 
 /* The same as flow_effect but with no input */
-static int drain_effect(unsigned e)
+static int drain_effect(unsigned n)
 {
+  sox_effect_t * effp = &sox_effects[n][0];
   int effstatus = SOX_SUCCESS;
   sox_size_t i, f;
-  sox_size_t odone = sox_bufsiz - sox_effects[e][0].olen;
+  sox_size_t odone = sox_bufsiz - effp->olen;
 
-  if (sox_effects[e][0].flows == 1)   /* Run effect on all channels at once */
-    effstatus = sox_effects[e][0].handler.drain(&sox_effects[e][0],
-      &sox_effects[e][0].obuf[sox_effects[e][0].olen], &odone);
+  if (effp->flows == 1)   /* Run effect on all channels at once */
+    effstatus = effp->handler.drain(effp, &effp->obuf[effp->olen], &odone);
   else {                         /* Run effect on each channel individually */
-    sox_ssample_t *obuf = &sox_effects[e][0].obuf[sox_effects[e][0].olen];
+    sox_ssample_t *obuf = &effp->obuf[effp->olen];
     sox_size_t odone_last;
 
-    for (f = 0; f < sox_effects[e][0].flows; ++f) {
-      sox_size_t odonec = odone / sox_effects[e][0].flows;
-      int eff_status_c =
-        sox_effects[e][0].handler.drain(&sox_effects[e][f], obufc[f], &odonec);
+    for (f = 0; f < effp->flows; ++f) {
+      sox_size_t odonec = odone / effp->flows;
+      int eff_status_c = effp->handler.drain(&sox_effects[n][f], obufc[f], &odonec);
       if (f && (odonec != odone_last)) {
-        sox_fail("'%s' drained asymmetrically!", sox_effects[e][0].handler.name);
+        sox_fail("drained asymmetrically!");
         effstatus = SOX_EOF;
       }
       odone_last = odonec;
@@ -324,7 +332,7 @@
     }
 
     for (i = 0; i < odone_last; ++i)
-      for (f = 0; f < sox_effects[e][0].flows; ++f)
+      for (f = 0; f < effp->flows; ++f)
         *obuf++ = obufc[f][i];
     odone = f * odone_last;
   }
@@ -331,19 +339,19 @@
   if (!odone)   /* This is the only thing that drain has and flow hasn't */
     effstatus = SOX_EOF;
 
-  sox_effects[e][0].olen += odone;
+  effp->olen += odone;
 
   return effstatus == SOX_SUCCESS? SOX_SUCCESS : SOX_EOF;
 }
 
-int sox_flow_effects(void (* update_status)(sox_bool), sox_bool * user_abort)
+int sox_flow_effects(int (* callback)(sox_bool all_done))
 {
-  int e, source_e = 0;               /* effect indices */
   int flow_status = SOX_SUCCESS;
-  sox_bool draining = sox_true;
+  sox_size_t e, source_e = 0;               /* effect indices */
   sox_size_t f, max_flows = 0;
+  sox_bool draining = sox_true;
 
-  for (e = 0; e < (int)sox_neffects; ++e) {
+  for (e = 0; e < sox_neffects; ++e) {
     sox_effects[e][0].obuf = xmalloc(sox_bufsiz * sizeof(sox_effects[e][0].obuf[0]));
     sox_effects[e][0].odone = sox_effects[e][0].olen = 0;
     max_flows = max(max_flows, sox_effects[e][0].flows);
@@ -356,27 +364,30 @@
     obufc[f] = xcalloc(sox_bufsiz / 2, sizeof(obufc[f][0]));
   }
 
-  --e;
-  while (source_e < (int)sox_neffects) {
-    if (e == source_e && (draining || sox_effects[e - 1][0].odone == sox_effects[e - 1][0].olen)) {
+  e = sox_neffects - 1;
+  while (source_e < sox_neffects) {
+#define have_imin (sox_effects[e - 1][0].olen - sox_effects[e - 1][0].odone >= sox_effects[e][0].imin)
+    if (e == source_e && (draining || !have_imin)) {
       if (drain_effect(e) == SOX_EOF) {
         ++source_e;
         draining = sox_false;
       }
-    } else if (flow_effect(e) == SOX_EOF) {
+    } else if (have_imin && flow_effect(e) == SOX_EOF) {
       flow_status = SOX_EOF;
       source_e = e;
       draining = sox_true;
     }
-    if (sox_effects[e][0].odone < sox_effects[e][0].olen)
+    if (sox_effects[e][0].olen > sox_effects[e][0].odone) /* False for output */
       ++e;
-    else if (--e < source_e)
+    else if (e == source_e)
+      draining = sox_true;
+    else if ((int)--e < (int)source_e)
       e = source_e;
 
-    update_status(*user_abort || source_e == (int)sox_neffects);
-    
-    if (*user_abort) /* Don't get stuck in this loop. */
-      return SOX_EOF;
+    if (callback && callback(source_e == sox_neffects) != SOX_SUCCESS) {
+      flow_status = SOX_EOF; /* Client has requested to stop the flow. */
+      break;
+    }
   }
 
   for (f = 0; f < max_flows; ++f) {
@@ -386,7 +397,7 @@
   free(obufc);
   free(ibufc);
 
-  for (e = 0; e < (int)sox_neffects; ++e)
+  for (e = 0; e < sox_neffects; ++e)
     free(sox_effects[e][0].obuf);
 
   return flow_status;
@@ -401,62 +412,9 @@
 
 /* Effects handlers. */
 
-/* FIXME: Generate this list automatically */
 sox_effect_fn_t sox_effect_fns[] = {
-  sox_allpass_effect_fn,
-  sox_avg_effect_fn,
-  sox_band_effect_fn,
-  sox_bandpass_effect_fn,
-  sox_bandreject_effect_fn,
-  sox_bass_effect_fn,
-  sox_chorus_effect_fn,
-  sox_compand_effect_fn,
-  sox_dcshift_effect_fn,
-  sox_deemph_effect_fn,
-  sox_dither_effect_fn,
-  sox_earwax_effect_fn,
-  sox_echo_effect_fn,
-  sox_echos_effect_fn,
-  sox_equalizer_effect_fn,
-  sox_fade_effect_fn,
-  sox_filter_effect_fn,
-  sox_flanger_effect_fn,
-  sox_highpass_effect_fn,
-  sox_highp_effect_fn,
-#ifdef HAVE_LADSPA_H
-  sox_ladspa_effect_fn,
-#endif
-  sox_lowpass_effect_fn,
-  sox_lowp_effect_fn,
-  sox_mask_effect_fn,
-  sox_mcompand_effect_fn,
-  sox_mixer_effect_fn,
-  sox_noiseprof_effect_fn,
-  sox_noisered_effect_fn,
-  sox_pad_effect_fn,
-  sox_pan_effect_fn,
-  sox_phaser_effect_fn,
-  sox_pick_effect_fn,
-  sox_pitch_effect_fn,
-  sox_polyphase_effect_fn,
-#ifdef HAVE_SAMPLERATE_H
-  sox_rabbit_effect_fn,
-#endif
-  sox_rate_effect_fn,
-  sox_repeat_effect_fn,
-  sox_resample_effect_fn,
-  sox_reverb_effect_fn,
-  sox_reverse_effect_fn,
-  sox_silence_effect_fn,
-  sox_speed_effect_fn,
-  sox_stat_effect_fn,
-  sox_stretch_effect_fn,
-  sox_swap_effect_fn,
-  sox_synth_effect_fn,
-  sox_treble_effect_fn,
-  sox_tremolo_effect_fn,
-  sox_trim_effect_fn,
-  sox_vibro_effect_fn,
-  sox_vol_effect_fn,
+#define EFFECT(f) sox_##f##_effect_fn,
+#include "effects.h"
+#undef EFFECT
   NULL
 };
--- /dev/null
+++ b/src/effects.h
@@ -1,0 +1,57 @@
+/* FIXME: generate this list automatically */
+
+  EFFECT(allpass)
+  EFFECT(avg)
+  EFFECT(band)
+  EFFECT(bandpass)
+  EFFECT(bandreject)
+  EFFECT(bass)
+  EFFECT(chorus)
+  EFFECT(compand)
+  EFFECT(dcshift)
+  EFFECT(deemph)
+  EFFECT(dither)
+  EFFECT(earwax)
+  EFFECT(echo)
+  EFFECT(echos)
+  EFFECT(equalizer)
+  EFFECT(fade)
+  EFFECT(filter)
+  EFFECT(flanger)
+  EFFECT(highpass)
+  EFFECT(highp)
+#ifdef HAVE_LADSPA_H
+  EFFECT(ladspa)
+#endif
+  EFFECT(lowpass)
+  EFFECT(lowp)
+  EFFECT(mask)
+  EFFECT(mcompand)
+  EFFECT(mixer)
+  EFFECT(noiseprof)
+  EFFECT(noisered)
+  EFFECT(pad)
+  EFFECT(pan)
+  EFFECT(phaser)
+  EFFECT(pick)
+  EFFECT(pitch)
+  EFFECT(polyphase)
+#ifdef HAVE_SAMPLERATE_H
+  EFFECT(rabbit)
+#endif
+  EFFECT(rate)
+  EFFECT(repeat)
+  EFFECT(resample)
+  EFFECT(reverb)
+  EFFECT(reverse)
+  EFFECT(silence)
+  EFFECT(speed)
+  EFFECT(stat)
+  EFFECT(stretch)
+  EFFECT(swap)
+  EFFECT(synth)
+  EFFECT(treble)
+  EFFECT(tremolo)
+  EFFECT(trim)
+  EFFECT(vibro)
+  EFFECT(vol)
--- a/src/sox.c
+++ b/src/sox.c
@@ -110,7 +110,7 @@
 static void usage(char const *) NORET;
 static void usage_effect(char *) NORET;
 static int process(void);
-static void update_status(sox_bool all_done);
+static void display_status(sox_bool all_done);
 static void report_file_info(file_t f);
 
 #define MAX_INPUT_FILES 32
@@ -609,7 +609,8 @@
   } while (process() != SOX_EOF && !user_abort && current_input < input_count);
   else process();
 
-  sox_kill_effects();
+  while (nuser_effects--)
+    user_efftab[nuser_effects].handler.kill(&user_efftab[nuser_effects]);
 
   for (i = 0; i < file_count; ++i)
     if (files[i]->desc->clips != 0)
@@ -1221,7 +1222,7 @@
 
   /* Default name should always be correct! */
   sox_create_effect(&e, sox_find_effect(name));
-  if (e.handler.getopts(&e, 0, NULL) == SOX_EOF)   /* Set up with default opts */
+  if (e.handler.getopts(&e, 0, NULL) == SOX_EOF)  /* Set up with default opts */
     exit(2);
   sox_add_effect(&e, &combiner, &ofile->desc->signal, effects_mask);
 }
@@ -1340,6 +1341,12 @@
   report_file_info(ofile);
 }
 
+static int update_status(sox_bool all_done)
+{
+  display_status(all_done || user_abort);
+  return user_abort? SOX_EOF : SOX_SUCCESS;
+}
+
 /*
  * Process:   Input(s) -> Balancing -> Combiner -> Effects -> Output
  */
@@ -1370,9 +1377,9 @@
       max_rate = max(max_rate, files[i]->desc->signal.rate);
       known_length = known_length && files[i]->desc->length != 0;
       if (combine_method == sox_concatenate)
-        olen += files[i]->desc->length / files[i]->desc->signal.channels;
+        olen += files[i]->desc->length;
       else
-        olen = max(olen, files[i]->desc->length / files[i]->desc->signal.channels);
+        olen = max(olen, files[i]->desc->length);
     }
     if (min_rate != max_rate)
       sox_fail("Input files must have the same sample-rate");
@@ -1419,7 +1426,7 @@
   signal(SIGINT, sigint);
   signal(SIGTERM, sigint);
 
-  flowstatus = sox_flow_effects(update_status, &user_abort);
+  flowstatus = sox_flow_effects(update_status);
 
   sox_stop_effects();
   sox_delete_effects();
@@ -1467,7 +1474,7 @@
   return string[n];
 }
 
-static void update_status(sox_bool all_done)
+static void display_status(sox_bool all_done)
 {
   static struct timeval then;
   if (!show_progress)
--- a/src/sox.h
+++ b/src/sox.h
@@ -381,8 +381,8 @@
 
   int (*getopts)(sox_effect_t * effp, int argc, char *argv[]);
   int (*start)(sox_effect_t * effp);
-  int (*flow)(sox_effect_t * effp, const sox_ssample_t *ibuf, sox_ssample_t *obuf,
-              sox_size_t *isamp, sox_size_t *osamp);
+  int (*flow)(sox_effect_t * effp, const sox_ssample_t *ibuf,
+      sox_ssample_t *obuf, sox_size_t *isamp, sox_size_t *osamp);
   int (*drain)(sox_effect_t * effp, sox_ssample_t *obuf, sox_size_t *osamp);
   int (*stop)(sox_effect_t * effp);
   int (*kill)(sox_effect_t * effp);
@@ -399,6 +399,7 @@
   sox_effect_handler_t     handler;
   sox_ssample_t            * obuf;       /* output buffer */
   sox_size_t               odone, olen;  /* consumed, total length */
+  sox_size_t               imin;         /* minimum input buffer size */
   sox_size_t               clips;        /* increment if clipping occurs */
   sox_size_t               flows;
 };
@@ -429,11 +430,11 @@
 #define SOX_MAX_EFFECTS 20
 extern sox_effect_t * sox_effects[SOX_MAX_EFFECTS];
 extern unsigned sox_neffects;
+int sox_effect_set_imin(sox_effect_t * effp, sox_size_t imin);
 int sox_add_effect(sox_effect_t * e, sox_signalinfo_t * in, sox_signalinfo_t * out, int * effects_mask);
 int sox_start_effects(void);
-int sox_flow_effects(void (* update_status)(sox_bool), sox_bool * user_abort);
+int sox_flow_effects(int (* callback)(sox_bool all_done));
 void sox_stop_effects(void);
-void sox_kill_effects(void);
 void sox_delete_effects(void);
 
 int sox_gettype(ft_t, sox_bool);
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -292,64 +292,10 @@
  */
 
 typedef const sox_effect_handler_t *(*sox_effect_fn_t)(void);
-
 extern sox_effect_fn_t sox_effect_fns[];
-
-extern const sox_effect_handler_t *sox_allpass_effect_fn(void);
-extern const sox_effect_handler_t *sox_avg_effect_fn(void);
-extern const sox_effect_handler_t *sox_band_effect_fn(void);
-extern const sox_effect_handler_t *sox_bandpass_effect_fn(void);
-extern const sox_effect_handler_t *sox_bandreject_effect_fn(void);
-extern const sox_effect_handler_t *sox_bass_effect_fn(void);
-extern const sox_effect_handler_t *sox_chorus_effect_fn(void);
-extern const sox_effect_handler_t *sox_compand_effect_fn(void);
-extern const sox_effect_handler_t *sox_dcshift_effect_fn(void);
-extern const sox_effect_handler_t *sox_deemph_effect_fn(void);
-extern const sox_effect_handler_t *sox_dither_effect_fn(void);
-extern const sox_effect_handler_t *sox_earwax_effect_fn(void);
-extern const sox_effect_handler_t *sox_echo_effect_fn(void);
-extern const sox_effect_handler_t *sox_echos_effect_fn(void);
-extern const sox_effect_handler_t *sox_equalizer_effect_fn(void);
-extern const sox_effect_handler_t *sox_fade_effect_fn(void);
-extern const sox_effect_handler_t *sox_filter_effect_fn(void);
-extern const sox_effect_handler_t *sox_flanger_effect_fn(void);
-extern const sox_effect_handler_t *sox_highpass_effect_fn(void);
-extern const sox_effect_handler_t *sox_highp_effect_fn(void);
-#ifdef HAVE_LADSPA_H
-extern const sox_effect_handler_t *sox_ladspa_effect_fn(void);
-#endif
-extern const sox_effect_handler_t *sox_lowpass_effect_fn(void);
-extern const sox_effect_handler_t *sox_lowp_effect_fn(void);
-extern const sox_effect_handler_t *sox_mask_effect_fn(void);
-extern const sox_effect_handler_t *sox_mcompand_effect_fn(void);
-extern const sox_effect_handler_t *sox_mixer_effect_fn(void);
-extern const sox_effect_handler_t *sox_noiseprof_effect_fn(void);
-extern const sox_effect_handler_t *sox_noisered_effect_fn(void);
-extern const sox_effect_handler_t *sox_pad_effect_fn(void);
-extern const sox_effect_handler_t *sox_pan_effect_fn(void);
-extern const sox_effect_handler_t *sox_phaser_effect_fn(void);
-extern const sox_effect_handler_t *sox_pick_effect_fn(void);
-extern const sox_effect_handler_t *sox_pitch_effect_fn(void);
-extern const sox_effect_handler_t *sox_polyphase_effect_fn(void);
-#ifdef HAVE_SAMPLERATE_H
-extern const sox_effect_handler_t *sox_rabbit_effect_fn(void);
-#endif
-extern const sox_effect_handler_t *sox_rate_effect_fn(void);
-extern const sox_effect_handler_t *sox_repeat_effect_fn(void);
-extern const sox_effect_handler_t *sox_resample_effect_fn(void);
-extern const sox_effect_handler_t *sox_reverb_effect_fn(void);
-extern const sox_effect_handler_t *sox_reverse_effect_fn(void);
-extern const sox_effect_handler_t *sox_silence_effect_fn(void);
-extern const sox_effect_handler_t *sox_speed_effect_fn(void);
-extern const sox_effect_handler_t *sox_stat_effect_fn(void);
-extern const sox_effect_handler_t *sox_stretch_effect_fn(void);
-extern const sox_effect_handler_t *sox_swap_effect_fn(void);
-extern const sox_effect_handler_t *sox_synth_effect_fn(void);
-extern const sox_effect_handler_t *sox_treble_effect_fn(void);
-extern const sox_effect_handler_t *sox_tremolo_effect_fn(void);
-extern const sox_effect_handler_t *sox_trim_effect_fn(void);
-extern const sox_effect_handler_t *sox_vibro_effect_fn(void);
-extern const sox_effect_handler_t *sox_vol_effect_fn(void);
+#define EFFECT(f) extern sox_effect_handler_t const * sox_##f##_effect_fn(void);
+#include "effects.h"
+#undef EFFECT
 
 /* Needed in rate.c */
 int sox_resample_start(sox_effect_t * effp);
--- a/src/synth.c
+++ b/src/synth.c
@@ -165,11 +165,13 @@
 /* Private data for the synthesizer */
 typedef struct {
   char *        length_str;
+  channel_t     getopts_channels;
+  sox_size_t    getopts_nchannels;
   sox_ssample_t max;
   sox_size_t    samples_done;
   sox_size_t    samples_to_do;
   channel_t     channels;
-  unsigned number_of_channels;
+  sox_size_t    number_of_channels;
 } * synth_t;
 
 
@@ -304,8 +306,8 @@
       sox_fail("no type given");
       return SOX_EOF;
     }
-    synth->channels = xrealloc(synth->channels, sizeof(*synth->channels) * (synth->number_of_channels + 1));
-    chan = &synth->channels[synth->number_of_channels++];
+    synth->getopts_channels = xrealloc(synth->getopts_channels, sizeof(*synth->getopts_channels) * (synth->getopts_nchannels + 1));
+    chan = &synth->getopts_channels[synth->getopts_nchannels++];
     create_channel(chan);
     chan->type = p->value;
     if (++argn == argc)
@@ -366,14 +368,14 @@
     } while (0);
   }
 
-  /* If no channels parameters were given, create one default channel: */
-  if (!synth->number_of_channels) {
-    synth->channels = xmalloc(sizeof(*synth->channels));
-    create_channel(&synth->channels[synth->number_of_channels++]);
+  /* If no channel parameters were given, create one default channel: */
+  if (!synth->getopts_nchannels) {
+    synth->getopts_channels = xmalloc(sizeof(*synth->getopts_channels));
+    create_channel(&synth->getopts_channels[synth->getopts_nchannels++]);
   }
 
   if (!effp->ininfo.channels)
-    effp->ininfo.channels = synth->number_of_channels;
+    effp->ininfo.channels = synth->getopts_nchannels;
 
   return SOX_SUCCESS;
 }
@@ -395,20 +397,12 @@
       return SOX_EOF;
     }
 
-  /* If too few channel parameters were given, copy channels: */
-  if (synth->number_of_channels < effp->ininfo.channels) {
-    synth->channels = xrealloc(synth->channels, sizeof(*synth->channels) * effp->ininfo.channels);
-    for (i = synth->number_of_channels; i < effp->ininfo.channels; ++i)
-      synth->channels[i] = synth->channels[i % synth->number_of_channels];
-    synth->number_of_channels = i;
-  }
-
-  for (i = 0; i < synth->number_of_channels; i++)
-    set_default_parameters(&synth->channels[i], i);
-
-  for (i = 0; i < effp->ininfo.channels; i++) {
+  synth->number_of_channels = effp->ininfo.channels;
+  synth->channels = xcalloc(synth->number_of_channels, sizeof(*synth->channels));
+  for (i = 0; i < synth->number_of_channels; ++i) {
     channel_t chan = &synth->channels[i];
-
+    *chan = synth->getopts_channels[i % synth->getopts_nchannels];
+    set_default_parameters(chan, i);
     sox_debug("type=%s, combine=%s, samples_to_do=%u, f1=%g, f2=%g, "
               "offset=%g, phase=%g, p1=%g, p2=%g, p3=%g",
         find_enum_value(chan->type, synth_type)->text,
@@ -583,10 +577,19 @@
 
 
 
-static int kill(sox_effect_t * effp)
+static int stop(sox_effect_t * effp)
 {
   synth_t synth = (synth_t) effp->priv;
   free(synth->channels);
+  return SOX_SUCCESS;
+}
+
+
+
+static int kill(sox_effect_t * effp)
+{
+  synth_t synth = (synth_t) effp->priv;
+  free(synth->getopts_channels);
   free(synth->length_str);
   return SOX_SUCCESS;
 }
@@ -598,7 +601,7 @@
   static sox_effect_handler_t handler = {
     "synth",
     "Usage: synth [len] {type [combine] [freq[-freq2] [off [ph [p1 [p2 [p3]]]]]]}",
-    SOX_EFF_MCHAN, getopts, start, flow, 0, 0, kill
+    SOX_EFF_MCHAN, getopts, start, flow, 0, stop, kill
   };
   return &handler;
 }