shithub: sox

Download patch

ref: 316506882236733bd91786a33fc8a9594dd1d8ff
parent: 95609fe504c606a09b660d2d1e27d4de2dd50f0c
author: cbagwell <cbagwell>
date: Fri Sep 24 19:15:37 EDT 2004

Adding mcompand file

--- a/src/8svx.c
+++ b/src/8svx.c
@@ -81,16 +81,16 @@
                                 st_fail_errno(ft, ST_EHDR, "VHDR chunk has bad size");
                                 return(ST_EOF);
                         }
-                        fseek(ft->fp,12,SEEK_CUR);
+                        st_seek(ft,12,SEEK_CUR);
                         st_readw(ft, &rate);
-                        fseek(ft->fp,1,SEEK_CUR);
-                        fread(buf,1,1,ft->fp);
+                        st_seek(ft,1,SEEK_CUR);
+                        st_read(ft, buf,1,1);
                         if (buf[0] != 0)
                         {
                                 st_fail_errno(ft, ST_EFMT, "Unsupported data compression");
                                 return(ST_EOF);
                         }
-                        fseek(ft->fp,4,SEEK_CUR);
+                        st_seek(ft,4,SEEK_CUR);
                         continue;
                 }
 
@@ -104,7 +104,7 @@
                             st_fail_errno(ft, ST_ENOMEM, "Unable to alloc memory");
                             return(ST_EOF);
                         }
-                        if (fread(chunk_buf,1,(size_t)chunksize,ft->fp) 
+                        if (st_read(ft, chunk_buf,1,(size_t)chunksize) 
                                         != chunksize)
                         {
                                 st_fail_errno(ft, ST_EHDR, "Couldn't read all of header");
@@ -127,7 +127,7 @@
                             st_fail_errno(ft, ST_ENOMEM, "Unable to alloc memory");
                             return(ST_EOF);
                         }
-                        if (fread (chunk_buf,1,(size_t)chunksize,ft->fp) 
+                        if (st_read(ft, chunk_buf,1,(size_t)chunksize) 
                                         != chunksize)
                         {
                                 st_fail_errno(ft, ST_EHDR, "Couldn't read all of header");
@@ -160,7 +160,7 @@
                 st_readdw(ft, &chunksize);
                 if (chunksize & 1)
                         chunksize++;
-                fseek(ft->fp,chunksize,SEEK_CUR);
+                st_seek(ft,chunksize,SEEK_CUR);
                 continue;
 
         }
@@ -326,13 +326,13 @@
         /* append all channel pieces to channel 0 */
         /* close temp files */
         for (i = 1; i < ft->info.channels; i++) {
-                if (fseek (p->ch[i], 0L, 0))
+                if (fseek(p->ch[i], 0L, 0))
                 {
                         st_fail_errno (ft,errno,"Can't rewind channel output file %d",i);
                         return(ST_EOF);
                 }
                 while (!feof(p->ch[i])) {
-                        len = fread (svxbuf, 1, 512, p->ch[i]);
+                        len = fread(svxbuf, 1, 512, p->ch[i]);
                         fwrite (svxbuf, 1, len, p->ch[0]);
                 }
                 fclose (p->ch[i]);
@@ -343,7 +343,7 @@
             st_writeb(ft, '\0');
 
         /* fixup file sizes in header */
-        if (fseek(ft->fp, 0L, 0) != 0)
+        if (st_seek(ft, 0L, 0) != 0)
         {
                 st_fail_errno(ft,errno,"can't rewind output file to rewrite 8SVX header");
                 return(ST_EOF);
--- /dev/null
+++ b/src/mcompand.c
@@ -1,0 +1,725 @@
+/*
+ * multiband compander effect for SoX
+ * by Daniel Pouzzner <douzzer@mega.nu> 2002-Oct-8
+ *
+ * Compander code adapted from the SoX compand effect, by Nick Bailey
+ *
+ * Butterworth code adapted from the SoX Butterworth effect family, by
+ * Jan Paul Schmidt
+ *
+ * SoX is Copyright 1999 Chris Bagwell And Nick Bailey
+ * This source code is freely redistributable and may be used for
+ * any purpose.  This copyright notice must be maintained. 
+ * Chris Bagwell And Nick Bailey are not responsible for 
+ * the consequences of using this software.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include "st_i.h"
+
+/*
+ * Usage:
+ *   mcompand quoted_compand_args [crossover_frequency
+ *      quoted_compand_args [...]]
+ *
+ *   quoted_compand_args are as for the compand effect:
+ *
+ *   attack1,decay1[,attack2,decay2...]
+ *                  in-dB1,out-dB1[,in-dB2,out-dB2...]
+ *                 [ gain [ initial-volume [ delay ] ] ] 
+ *
+ *   Beware a variety of headroom (clipping) bugaboos.
+ *
+ *   Here is an example application, an FM radio sound simulator (or
+ *   broadcast signal conditioner, if the lowp at the end is skipped -
+ *   note that the pipeline is set up with US-style 75us preemphasis).
+ *
+ *   sox -V -t raw -r 44100 -s -w -c 2 - -t raw -r 44100 -s -l -c 2 \
+ *      - vol -3 db filter 8000- 32 100 mcompand ".005,.1 \
+ *      -47,-40,-34,-34,-17,-33 0 0 0" 100 ".003,.05 \
+ *      -47,-40,-34,-34,-17,-33 0 0 0" 400 ".000625,.0125 \
+ *      -47,-40,-34,-34,-15,-33 0 0 0" 1600 ".0001,.025 \
+ *      -47,-40,-34,-34,-31,-31,-0,-30 0 0 0" 6400 \
+ *      "0,.025 -38,-31,-28,-28,-0,-25 0 0 0" vol 27 db vol -12 \
+ *      db highpass 22 highpass 22 filter -17500 256 vol +12 db \
+ *      vol -3 db lowp 1780
+ *
+ *   implementation details:
+ *
+ *   The input is divided into bands using aligned 2nd order
+ *   Butterworth IIR's (code adapted from SoX's existing Butterworth
+ *   effect).  This is akin to the crossover of a loudspeaker, and
+ *   results in flat frequency response (within a db or so) absent
+ *   compander action.
+ *
+ *   (Crossover design from "Electroacoustic System Design" by
+ *   Tom Raymann, http://www.traymann.free-online.co.uk/soph.htm)
+ *
+ *   The imported Butterworth code is modified to handle
+ *   interleaved-channel sample streams, to make the code clean and
+ *   efficient.  The outputs of the array of companders is summed, and
+ *   sample truncation is done on the final sum.
+ *
+ *   Modifications to the predictive compression code properly
+ *   maintain alignment of the outputs of the array of companders when
+ *   the companders have different prediction intervals (volume
+ *   application delays).  Note that the predictive mode of the
+ *   limiter needs some TLC - in fact, a rewrite - since what's really
+ *   useful is to assure that a waveform won't be clipped, be slewing
+ *   the volume in advance so that the peak is at limit (or below, if
+ *   there's a higher subsequent peak visible in the lookahead window)
+ *   once it's reached.  */
+
+typedef struct butterworth_crossover {
+  struct xy {
+    double x [2];
+    double y [2];
+  } *xy_low, *xy_high;
+
+  double a_low[3], a_high[3];
+  double b_low[2], b_high[3];
+
+  /*
+   * Cut off frequencies for respective filters
+   */
+  double frequency_low, frequency_high;
+
+  double bandwidth;
+} *butterworth_crossover_t;
+
+static int lowpass_setup (butterworth_crossover_t butterworth, double frequency, st_rate_t rate, int nchan) {
+  double c;
+
+  if (! (butterworth->xy_low = (struct xy *)malloc(nchan * sizeof(struct xy)))) {
+    st_fail("Out of memory");
+    return (ST_EOF);
+  }
+  memset(butterworth->xy_low,0,nchan * sizeof(struct xy));
+  if (! (butterworth->xy_high = (struct xy *)malloc(nchan * sizeof(struct xy)))) {
+    st_fail("Out of memory");
+    return (ST_EOF);
+  }
+  memset(butterworth->xy_high,0,nchan * sizeof(struct xy));
+
+  /* lowpass setup */
+  butterworth->frequency_low = frequency/1.3;
+
+  c = 1.0 / tan (M_PI * butterworth->frequency_low / rate);
+
+  butterworth->a_low[0] = 1.0 / (1.0 + sqrt(2.0) * c + c * c);
+  butterworth->a_low[1] = 2.0 * butterworth->a_low [0];
+  butterworth->a_low[2] = butterworth->a_low [0];
+
+  butterworth->b_low[0] = 2 * (1.0 - c * c) * butterworth->a_low[0];
+  butterworth->b_low[1] = (1.0 - sqrt(2.0) * c + c * c) * butterworth->a_low[0];
+
+  /* highpass setup */
+  butterworth->frequency_high = frequency*1.3;
+
+  c = tan (M_PI * butterworth->frequency_high / rate);
+
+  butterworth->a_high[0] = 1.0 / (1.0 + sqrt (2.0) * c + c * c);
+  butterworth->a_high[1] = -2.0 * butterworth->a_high[0];
+  butterworth->a_high[2] = butterworth->a_high[0];
+
+  butterworth->b_high[0] = 2 * (c * c - 1.0) * butterworth->a_high[0];
+  butterworth->b_high[1] = (1.0 - sqrt(2.0) * c + c * c) * butterworth->a_high[0];
+
+  return (ST_SUCCESS);
+}
+
+static int lowpass_flow(butterworth_crossover_t butterworth, int nChan, st_sample_t *ibuf, st_sample_t *lowbuf, st_sample_t *highbuf,
+                         int len) {
+  int chan;
+  double in, out;
+
+  int done;
+
+  st_sample_t *ibufptr, *lowbufptr, *highbufptr;
+
+  for (chan=0;chan<nChan;++chan) {
+    ibufptr = ibuf+chan;
+    lowbufptr = lowbuf+chan;
+    highbufptr = highbuf+chan;
+
+    for (done = chan; done < len; done += nChan) {
+      in = *ibufptr;
+      ibufptr += nChan;
+
+      /*
+       * Substituting butterworth->a [x] and butterworth->b [x] with
+       * variables, which are set outside of the loop, did not increased
+       * speed on my AMD Box. GCC seems to do a good job :o)
+       */
+
+      out =
+        butterworth->a_low[0] * in +
+        butterworth->a_low [1] * butterworth->xy_low[chan].x [0] +
+        butterworth->a_low [2] * butterworth->xy_low[chan].x [1] -
+        butterworth->b_low [0] * butterworth->xy_low[chan].y [0] -
+        butterworth->b_low [1] * butterworth->xy_low[chan].y [1];
+
+      butterworth->xy_low[chan].x [1] = butterworth->xy_low[chan].x [0];
+      butterworth->xy_low[chan].x [0] = in;
+      butterworth->xy_low[chan].y [1] = butterworth->xy_low[chan].y [0];
+      butterworth->xy_low[chan].y [0] = out;
+
+      if (out < ST_SAMPLE_MIN) {
+        out = ST_SAMPLE_MIN;
+      }
+      else if (out > ST_SAMPLE_MAX) {
+        out = ST_SAMPLE_MAX;
+      }
+
+      *lowbufptr = out;
+
+
+      out =
+        butterworth->a_high[0] * in +
+        butterworth->a_high [1] * butterworth->xy_high[chan].x [0] +
+        butterworth->a_high [2] * butterworth->xy_high[chan].x [1] -
+        butterworth->b_high [0] * butterworth->xy_high[chan].y [0] -
+        butterworth->b_high [1] * butterworth->xy_high[chan].y [1];
+
+      butterworth->xy_high[chan].x [1] = butterworth->xy_high[chan].x [0];
+      butterworth->xy_high[chan].x [0] = in;
+      butterworth->xy_high[chan].y [1] = butterworth->xy_high[chan].y [0];
+      butterworth->xy_high[chan].y [0] = out;
+
+      if (out < ST_SAMPLE_MIN) {
+        out = ST_SAMPLE_MIN;
+      }
+      else if (out > ST_SAMPLE_MAX) {
+        out = ST_SAMPLE_MAX;
+      }
+
+      /* don't forget polarity reversal of high pass! */
+
+      *highbufptr = -out;
+
+      lowbufptr += nChan;
+      highbufptr += nChan;
+    }
+  }
+
+  return (ST_SUCCESS);
+}
+
+typedef struct comp_band {
+  int expectedChannels; /* Also flags that channels aren't to be treated
+                           individually when = 1 and input not mono */
+  int transferPoints;   /* Number of points specified on the transfer
+                           function */
+  double *attackRate;   /* An array of attack rates */
+  double *decayRate;    /*    ... and of decay rates */
+  double *transferIns;  /*    ... and points on the transfer function */
+  double *transferOuts;
+  double *volume;       /* Current "volume" of each channel */
+  double outgain;       /* Post processor gain */
+  double delay;         /* Delay to apply before companding */
+  double topfreq;       /* upper bound crossover frequency */
+  struct butterworth_crossover filter;
+  st_sample_t *delay_buf;   /* Old samples, used for delay processing */
+  st_ssize_t delay_size;    /* lookahead for this band (in samples) - function of delay, above */
+  st_ssize_t delay_buf_ptr; /* Index into delay_buf */
+  st_ssize_t delay_buf_cnt; /* No. of active entries in delay_buf */
+} *comp_band_t;
+
+typedef struct {
+  int nBands;
+  st_sample_t *band_buf1, *band_buf2, *band_buf3;
+  int band_buf_len;
+  st_ssize_t delay_buf_size;/* Size of delay_buf in samples */
+  struct comp_band *bands;
+} *compand_t;
+
+/*
+ * Process options
+ *
+ * Don't do initialization now.
+ * The 'info' fields are not yet filled in.
+ */
+static int st_mcompand_getopts_1(comp_band_t l, int n, char **argv)
+{
+      char *s;
+      int rates, tfers, i, commas;
+
+      /* Start by checking the attack and decay rates */
+
+      for (s = argv[0], commas = 0; *s; ++s)
+        if (*s == ',') ++commas;
+
+      if (commas % 2 == 0) /* There must be an even number of
+                              attack/decay parameters */
+      {
+        st_fail("compander: Odd number of attack & decay rate parameters");
+        return (ST_EOF);
+      }
+
+      rates = 1 + commas/2;
+      if ((l->attackRate = malloc(sizeof(double) * rates)) == NULL ||
+          (l->decayRate  = malloc(sizeof(double) * rates)) == NULL)
+      {
+        st_fail("Out of memory");
+        return (ST_EOF);
+      }
+
+      if ((l->volume = malloc(sizeof(double) * rates)) == NULL)
+      {
+        st_fail("Out of memory");
+        return (ST_EOF);
+      }
+
+      l->expectedChannels = rates;
+
+      l->delay_buf = NULL;
+
+      /* Now tokenise the rates string and set up these arrays.  Keep
+         them in seconds at the moment: we don't know the sample rate yet. */
+
+      s = strtok(argv[0], ","); i = 0;
+      do {
+        l->attackRate[i] = atof(s); s = strtok(NULL, ",");
+        l->decayRate[i]  = atof(s); s = strtok(NULL, ",");
+        ++i;
+      } while (s != NULL);
+
+      /* Same business, but this time for the transfer function */
+
+      for (s = argv[1], commas = 0; *s; ++s)
+        if (*s == ',') ++commas;
+
+      if (commas % 2 == 0) /* There must be an even number of
+                              transfer parameters */
+      {
+        st_fail("compander: Odd number of transfer function parameters\n"
+             "Each input value in dB must have a corresponding output value");
+        return (ST_EOF);
+      }
+
+      tfers = 3 + commas/2; /* 0, 0 at start; 1, 1 at end */
+      if ((l->transferIns  = malloc(sizeof(double) * tfers)) == NULL ||
+          (l->transferOuts = malloc(sizeof(double) * tfers)) == NULL)
+      {
+        st_fail("Out of memory");
+        return (ST_EOF);
+      }
+      l->transferPoints = tfers;
+      l->transferIns[0] = 0.0; l->transferOuts[0] = 0.0;
+      l->transferIns[tfers-1] = 1.0; l->transferOuts[tfers-1] = 1.0;
+      s = strtok(argv[1], ","); i = 1;
+      do {
+        if (!strcmp(s, "-inf"))
+        {
+          st_fail("Input signals of zero level must always generate zero output");
+          return (ST_EOF);
+        }
+        l->transferIns[i]  = pow(10.0, atof(s)/20.0);
+        if (l->transferIns[i] > 1.0)
+        {
+          st_fail("dB values are relative to maximum input, and, ipso facto, "
+               "cannot exceed 0");
+          return (ST_EOF);
+        }
+        if (l->transferIns[i] == 1.0) /* Final point was explicit */
+          --(l->transferPoints);
+        if (i > 0 && l->transferIns[i] <= l->transferIns[i-1])
+        {
+          st_fail("Transfer function points don't have strictly ascending "
+               "input amplitude");
+          return (ST_EOF);
+        }
+        s = strtok(NULL, ",");
+        l->transferOuts[i] = strcmp(s, "-inf") ?
+                               pow(10.0, atof(s)/20.0) : 0;
+        s = strtok(NULL, ",");
+        ++i;
+      } while (s != NULL);
+      
+      /* If there is a postprocessor gain, store it */
+      if (n >= 3) l->outgain = pow(10.0, atof(argv[2])/20.0);
+      else l->outgain = 1.0;
+
+      /* Set the initial "volume" to be attibuted to the input channels.
+         Unless specified, choose 1.0 (maximum) otherwise clipping will
+         result if the user has seleced a long attack time */
+      for (i = 0; i < l->expectedChannels; ++i) {
+        double v = n>=4 ? pow(10.0, atof(argv[3])/20) : 1.0;
+        l->volume[i] = v;
+
+        /* If there is a delay, store it. */
+        if (n >= 5) l->delay = atof(argv[4]);
+        else l->delay = 0.0;
+      }
+    return (ST_SUCCESS);
+}
+
+static int parse_subarg(char *s,char **subargv, int *subargc) {
+  char **ap;
+
+  *subargc = 0;
+  for (ap = subargv; (*ap = strsep(&s, " \t")) != NULL;) {
+    if (*subargc == 5) {
+      ++*subargc;
+      break;
+    }
+    if (**ap != '\0') {
+      ++ap;
+      ++*subargc;
+    }
+  }
+
+  if (*subargc < 2 || *subargc > 5)
+    {
+      st_fail("Wrong number of arguments for the compander effect within mcompand\n"
+           "Use: {<attack_time>,<decay_time>}+ {<dB_in>,<db_out>}+ "
+           "[<dB_postamp> [<initial-volume> [<delay_time]]]\n"
+           "where {}+ means `one or more in a comma-separated, "
+           "white-space-free list'\n"
+           "and [] indications possible omission.  dB values are floating\n"
+           "point or `-inf'; times are in seconds.");
+      return (ST_EOF);
+    } else
+      return ST_SUCCESS;
+}
+
+int st_mcompand_getopts(eff_t effp, int n, char **argv) 
+{
+  char *subargv[6], *cp;
+  int subargc, i, len;
+
+  compand_t c = (compand_t) effp->priv;
+
+  c->band_buf1 = c->band_buf2 = c->band_buf3 = 0;
+  c->band_buf_len = 0;
+
+  /* how many bands? */
+  if (! (n&1)) {
+    st_fail("mcompand accepts only an odd number of arguments:\n"
+            "  mcompand quoted_compand_args [xover_freq quoted_compand_args [...]");
+    return ST_EOF;
+  }
+  c->nBands = (n+1)>>1;
+
+  if (! (c->bands = (struct comp_band *)malloc(c->nBands * sizeof(struct comp_band)))) {
+    st_fail("Out of memory");
+    return ST_EOF;
+  }
+  memset(c->bands,0,c->nBands * sizeof(struct comp_band));
+
+  for (i=0;i<c->nBands;++i) {
+    len = strlen(argv[i<<1]);
+    if (parse_subarg(argv[i<<1],subargv,&subargc) != ST_SUCCESS)
+      return ST_EOF;
+    if (st_mcompand_getopts_1(&c->bands[i], subargc, &subargv[0]) != ST_SUCCESS)
+      return ST_EOF;
+    if (i == (c->nBands-1))
+      c->bands[i].topfreq = 0;
+    else {
+      c->bands[i].topfreq = strtod(argv[(i<<1)+1],&cp);
+      if (*cp) {
+        st_fail("bad frequency in args to mcompand");
+        return ST_EOF;
+      }
+      if ((i>0) && (c->bands[i].topfreq < c->bands[i-1].topfreq)) {
+        st_fail("mcompand crossover frequencies must be in ascending order.");
+        return ST_EOF;
+      }
+    }
+  }
+
+  return ST_SUCCESS;
+}
+
+/*
+ * Prepare processing.
+ * Do all initializations.
+ */
+int st_mcompand_start(eff_t effp)
+{
+  compand_t c = (compand_t) effp->priv;
+  comp_band_t l;
+  int band, i;
+  
+  for (band=0;band<c->nBands;++band) {
+    l = &c->bands[band];
+    l->delay_size = c->bands[band].delay * effp->outinfo.rate * effp->outinfo.channels;
+    if (l->delay_size > c->delay_buf_size)
+      c->delay_buf_size = l->delay_size;
+  }
+
+  for (band=0;band<c->nBands;++band) {
+    l = &c->bands[band];
+    /* Convert attack and decay rates using number of samples */
+
+    for (i = 0; i < l->expectedChannels; ++i) {
+      if (l->attackRate[i] > 1.0/effp->outinfo.rate)
+        l->attackRate[i] = 1.0 -
+          exp(-1.0/(effp->outinfo.rate * l->attackRate[i]));
+      else
+        l->attackRate[i] = 1.0;
+      if (l->decayRate[i] > 1.0/effp->outinfo.rate)
+        l->decayRate[i] = 1.0 -
+          exp(-1.0/(effp->outinfo.rate * l->decayRate[i]));
+      else
+        l->decayRate[i] = 1.0;
+    }
+
+    /* Allocate the delay buffer */
+    if (c->delay_buf_size > 0) {
+      if ((l->delay_buf = malloc(sizeof(long) * c->delay_buf_size)) == NULL) {
+        st_fail("Out of memory");
+        return (ST_EOF);
+      }
+      for (i = 0;  i < c->delay_buf_size;  i++)
+        l->delay_buf[i] = 0;
+
+    }
+    l->delay_buf_ptr = 0;
+    l->delay_buf_cnt = 0;
+
+    if (l->topfreq != 0)
+      lowpass_setup(&l->filter, l->topfreq, effp->outinfo.rate, effp->outinfo.channels);
+  }
+  return (ST_SUCCESS);
+}
+
+/*
+ * Update a volume value using the given sample
+ * value, the attack rate and decay rate
+ */
+
+static void doVolume(double *v, double samp, comp_band_t l, int chan)
+{
+  double s = samp/(~((st_sample_t)1<<31));
+  double delta = s - *v;
+
+  if (delta > 0.0) /* increase volume according to attack rate */
+    *v += delta * l->attackRate[chan];
+  else             /* reduce volume according to decay rate */
+    *v += delta * l->decayRate[chan];
+}
+
+static int st_mcompand_flow_1(compand_t c, comp_band_t l, st_sample_t *ibuf, st_sample_t *obuf, int len, int filechans)
+{
+  int done, chan;
+
+  for (done = 0; done < len; ibuf += filechans) {
+
+    /* Maintain the volume fields by simulating a leaky pump circuit */
+
+    if (l->expectedChannels == 1 && filechans > 1) {
+      /* User is expecting same compander for all channels */
+      double maxsamp = 0.0;
+      for (chan = 0; chan < filechans; ++chan) {
+        double rect = fabs(ibuf[chan]);
+        if (rect > maxsamp)
+          maxsamp = rect;
+      }
+      doVolume(&l->volume[0], maxsamp, l, 0);
+    } else {
+      for (chan = 0; chan < filechans; ++chan)
+        doVolume(&l->volume[chan], fabs(ibuf[chan]), l, chan);
+    }
+
+    /* Volume memory is updated: perform compand */
+
+    for (chan = 0; chan < filechans; ++chan) {
+      double v = l->expectedChannels > 1 ? 
+        l->volume[chan] : l->volume[0];
+      double outv;
+      int piece;
+
+      for (piece = 1 /* yes, 1 */;
+           piece < l->transferPoints;
+           ++piece)
+        if (v >= l->transferIns[piece - 1] &&
+            v < l->transferIns[piece])
+          break;
+      
+      outv = l->transferOuts[piece-1] +
+        (l->transferOuts[piece] - l->transferOuts[piece-1]) *
+        (v - l->transferIns[piece-1]) /
+        (l->transferIns[piece] - l->transferIns[piece-1]);
+
+      if (c->delay_buf_size <= 0)
+        obuf[done++] = ibuf[chan]*(outv/v)*l->outgain;
+      else {
+
+
+        /* note that this lookahead algorithm is really lame.  the response to a peak is released before the peak arrives.  fix! */
+
+        /* because volume application delays differ band to band, but
+           total delay doesn't, the volume is applied in an iteration
+           preceding that in which the sample goes to obuf, except in
+           the band(s) with the longest vol app delay.
+
+           the offset between delay_buf_ptr and the sample to apply
+           vol to, is a constant equal to the difference between this
+           band's delay and the longest delay of all the bands. */
+
+        if (l->delay_buf_cnt >= l->delay_size)
+          l->delay_buf[(l->delay_buf_ptr + c->delay_buf_size - l->delay_size)%c->delay_buf_size] *= (outv/v)*l->outgain;
+        if (l->delay_buf_cnt >= c->delay_buf_size)
+          obuf[done++] = l->delay_buf[l->delay_buf_ptr];
+        else
+          l->delay_buf_cnt++;
+        l->delay_buf[l->delay_buf_ptr++] = ibuf[chan];
+        l->delay_buf_ptr %= c->delay_buf_size;
+      }
+    }
+  }
+
+  return (ST_SUCCESS);
+}
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ * Return number of samples processed.
+ */
+int st_mcompand_flow(eff_t effp, st_sample_t *ibuf, st_sample_t *obuf, 
+                     st_size_t *isamp, st_size_t *osamp) {
+  compand_t c = (compand_t) effp->priv;
+  comp_band_t l;
+  int len = ((*isamp > *osamp) ? *osamp : *isamp);
+  int band, i;
+  st_sample_t *abuf, *bbuf, *cbuf, *oldabuf;
+
+  if (c->band_buf_len < len) {
+    if ((! (c->band_buf1 = (st_sample_t *)realloc(c->band_buf1,len*sizeof(st_sample_t)))) ||
+        (! (c->band_buf2 = (st_sample_t *)realloc(c->band_buf2,len*sizeof(st_sample_t)))) ||
+        (! (c->band_buf3 = (st_sample_t *)realloc(c->band_buf3,len*sizeof(st_sample_t))))) {
+      st_fail("Out of memory");
+      return (ST_EOF);
+    }
+    c->band_buf_len = len;
+  }
+
+  /* split ibuf into bands using butterworths, pipe each band through st_mcompand_flow_1, then add back together and write to obuf */
+
+  memset(obuf,0,len * sizeof *obuf);
+  for (band=0,abuf=ibuf,bbuf=c->band_buf2,cbuf=c->band_buf1;band<c->nBands;++band) {
+    l = &c->bands[band];
+
+    if (l->topfreq)
+      lowpass_flow(&l->filter, effp->outinfo.channels, abuf, bbuf, cbuf, len);
+    else {
+      bbuf = abuf;
+      abuf = cbuf;
+    }
+    if (abuf == ibuf)
+      abuf = c->band_buf3;
+    (void)st_mcompand_flow_1(c,l,bbuf,abuf,len,effp->outinfo.channels);
+    for (i=0;i<len;++i)
+      obuf[i] += abuf[i];
+    oldabuf = abuf;
+    abuf = cbuf;
+    cbuf = oldabuf;
+  }
+
+  for (i=0;i<len;++i) {
+    if (obuf[i] < ST_SAMPLE_MIN) {
+      obuf[i] = ST_SAMPLE_MIN;
+    }
+    else if (obuf[i] > ST_SAMPLE_MAX) {
+      obuf[i] = ST_SAMPLE_MAX;
+    }
+  }
+
+  *isamp = *osamp = len;
+
+  return ST_SUCCESS;
+}
+
+static int st_mcompand_drain_1(compand_t c, comp_band_t l, st_sample_t *obuf, int maxdrain, int band)
+{
+  int done;
+
+  /*
+   * Drain out delay samples.  Note that this loop does all channels.
+   */
+  for (done = 0;  done < maxdrain  &&  l->delay_buf_cnt > 0;  done++) {
+    obuf[done] += l->delay_buf[l->delay_buf_ptr++];
+    l->delay_buf_ptr %= c->delay_buf_size;
+    l->delay_buf_cnt--;
+  }
+
+  /* tell caller number of samples played */
+  return done;
+
+}
+
+/*
+ * Drain out compander delay lines. 
+ */
+int st_mcompand_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
+{
+  int band, drained, mostdrained = 0;
+  compand_t c = (compand_t)effp->priv;
+  comp_band_t l;
+  int i;
+
+  memset(obuf,0,*osamp * sizeof *obuf);
+  for (band=0;band<c->nBands;++band) {
+    l = &c->bands[band];
+    drained = st_mcompand_drain_1(c,l,obuf,*osamp,0);
+    if (drained > mostdrained)
+      mostdrained = drained;
+  }
+
+  for (i=0;i<mostdrained;++i) {
+    if (obuf[i] < ST_SAMPLE_MIN) {
+      obuf[i] = ST_SAMPLE_MIN;
+    }
+    else if (obuf[i] > ST_SAMPLE_MAX) {
+      obuf[i] = ST_SAMPLE_MAX;
+    }
+  }
+
+  *osamp = mostdrained;
+  return (ST_SUCCESS);
+}
+
+/*
+ * Clean up compander effect.
+ */
+int st_mcompand_stop(eff_t effp)
+{
+  compand_t c = (compand_t) effp->priv;
+  comp_band_t l;
+  int band;
+
+  if (c->band_buf1) {
+    free(c->band_buf1);
+    c->band_buf1 = 0;
+  }
+  if (c->band_buf2) {
+    free(c->band_buf2);
+    c->band_buf2 = 0;
+  }
+  if (c->band_buf3) {
+    free(c->band_buf3);
+    c->band_buf3 = 0;
+  }
+
+  for (band=0;band<c->nBands;++band) {
+    l = &c->bands[band];
+    free((char *) l->transferOuts);
+    free((char *) l->transferIns);
+    free((char *) l->decayRate);
+    free((char *) l->attackRate);
+    if (l->delay_buf)
+      free((char *) l->delay_buf);
+    free((char *) l->volume);
+    if (l->topfreq != 0) {
+      free(l->filter.xy_low);
+      free(l->filter.xy_high);
+    }
+  }
+  free(c->bands);
+  c->bands = 0;
+
+  return (ST_SUCCESS);
+}