shithub: sox

Download patch

ref: 8de54df09bdcce676c59651ba9e6661ad2ad5d85
parent: 880f442273d4bf1c510bae8b4abb2befbd687bd6
author: robs <robs>
date: Sat Dec 9 17:45:33 EST 2006

New flanger & rationalise sine tables generation

--- a/AUTHORS
+++ b/AUTHORS
@@ -89,5 +89,5 @@
 		Much cleaning up, Secret Rabbit Code resampling.
 	Rob Sykes		robs@users.sourceforge.net
 		Bass & treble effects, FLAC support, initial 24bit
-		support, Octave plotting for filters,
+		support, Octave plotting for filters, new flanger,
 		various small fixes, enchancements and clean-ups.
--- a/sox.1
+++ b/sox.1
@@ -1074,13 +1074,38 @@
 For more discussion of beta, look under the \fBresample\fR effect.
 
 .TP 10
-flanger \fIgain-in gain-out delay decay speed\fR < -s | -t >
-Add a flanger to a sound sample.  Each triple
-delay/decay/speed gives the delay in milliseconds
-and the decay (relative to gain-in) with a modulation
-speed in Hz.
-The modulation is either sinusoidal (-s) or triangular
-(-t).  Gain-out is the volume of the output.
+flanger [\fIdelay depth regen width speed shape phase interp\fR]
+Apply a flanging effect to the signal.
+All parameters are optional (right to left).
+
+       RANGE DEFAULT DESCRIPTION
+.RS
+.TP 21
+\fIdelay\fR   0 10    0
+Base delay in milliseconds.
+.TP 21
+\fIdepth\fR   0 10    2
+Added swept delay in milliseconds.
+.TP 21
+\fIregen\fR -95 +95   0
+Percentage regeneration (delayed signal feedback).
+.TP 21
+\fIwidth\fR   0 100   71
+Percentage of delayed signal mixed with original.
+.TP 21
+\fIspeed\fR  0.1 10  0.5
+Sweeps per second (Hz).
+.TP 21
+\fIshape\fR    --    sin
+Swept wave shape: sine | triangle.
+.TP 21
+\fIphase\fR   0 100   25
+Swept wave percentage phase-shift for multi-channel
+(e.g. stereo) flange; 0 = 100 = same phase on each channel.
+.TP 21
+\fIinterp\fR   --    lin
+Delay-line interpolation: linear | quadratic.
+.RE
 .TP 10
 highp|lowp \fIfrequency\fR
 Apply a single-pole recursive high-pass or low-pass filter with
--- a/soxexam.1
+++ b/soxexam.1
@@ -340,28 +340,17 @@
 The flanger effect is widely used in funk and soul music, where the guitar 
 sound varies frequently slow or a bit faster.
 .P
-The typical delay is around 3ms to 5ms, the speed of the modulation is best
-near 0.5Hz.
+Enter \fIsox --help-effect flanger\fR to see the default settings.
 .P
 Now, let's groove the sample:
 .P
 .BR
-	play file.xxx flanger 0.6 0.87 3.0 0.9 0.5 -s
+	play file.xxx flanger
 .P
 listen carefully between the difference of sinusoidal and triangular modulation:
 .P
 .BR
-	play file.xxx flanger 0.6 0.87 3.0 0.9 0.5 -t
-.P
-If the decay is a bit lower, than the effect sounds more popular:
-.P
-.BR
-	play file.xxx flanger 0.8 0.88 3.0 0.4 0.5 -t
-.P
-The drunken loudspeaker system:
-.P
-.BR
-	play file.xxx flanger 0.9 0.9 4.0 0.23 1.3 -s
+	play file.xxx flanger triangle
 .P
 .B Reverb
 .P
--- a/src/chorus.c
+++ b/src/chorus.c
@@ -213,14 +213,14 @@
                                 sizeof(int) * chorus->length[i]);
                         return (ST_EOF);
                 }
-                if ( chorus->modulation[i] == MOD_SINE )
-                        st_sine(chorus->lookup_tab[i], chorus->length[i], 
-                                chorus->depth_samples[i] - 1,
-                                chorus->depth_samples[i]);
-                else
-                        st_triangle(chorus->lookup_tab[i], chorus->length[i], 
-                                chorus->samples[i] - 1,
-                                chorus->depth_samples[i]);
+    if (chorus->modulation[i] == MOD_SINE)
+      st_generate_wave_table(ST_WAVE_SINE, ST_INT, chorus->lookup_tab[i],
+          chorus->length[i], 0, chorus->depth_samples[i], 0);
+    else
+      st_generate_wave_table(ST_WAVE_TRIANGLE, ST_INT, chorus->lookup_tab[i], 
+          chorus->length[i],
+          chorus->samples[i] - 1 - 2 * chorus->depth_samples[i],
+          chorus->samples[i] - 1, 3 * M_PI_2);
                 chorus->phase[i] = 0;
 
                 if ( chorus->samples[i] > chorus->maxsamples )
--- a/src/flanger.c
+++ b/src/flanger.c
@@ -1,306 +1,326 @@
-/*
- * August 24, 1998
- * Copyright (C) 1998 Juergen Mueller And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose.  This copyright notice must be maintained. 
- * Juergen Mueller And Sundry Contributors are not responsible for 
- * the consequences of using this software.
- */
-
-/*
- *      Flanger effect.
- * 
- * Flow diagram scheme:
+/*  Sound Tools Effect: Stereo Flanger
  *
- *                                                 * gain-in  ___
- * ibuff -----+--------------------------------------------->|   |
- *            |      _______                                 |   |
- *            |     |       |                      * decay   |   |
- *            +---->| delay |------------------------------->| + |
- *                  |_______|                                |   |
- *                     /|\                                   |   |
- *                      |                                    |___|
- *                      |                                      | 
- *              +---------------+      +------------------+    | * gain-out
- *              | Delay control |<-----| modulation speed |    |
- *              +---------------+      +------------------+    +----->obuff
+ *  (c) 2006 robs@users.sourceforge.net
  *
- *
- * The delay is controled by a sine or triangle modulation.
- *
- * Usage: 
- *   flanger gain-in gain-out delay decay speed [ -s | -t ]
- *
- * Where:
- *   gain-in, decay :  0.0 ... 1.0      volume
- *   gain-out :  0.0 ...      volume
- *   delay :  0.0 ... 5.0 msec
- *   speed :  0.1 ... 2.0 Hz       modulation
- *   -s : modulation by sine (default)
- *   -t : modulation by triangle
- *
- * Note:
- *   when decay is close to 1.0, the samples may begin clipping or the output
- *   can saturate! 
- *
- * Hint:
- *   1 / out-gain > gain-in * ( 1 + decay )
- *
-*/
-
-/*
- * Sound Tools flanger effect file.
+ *  See LICENSE file for further copyright information.
  */
 
-#include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
+static char const st_flanger_usage[] =
+  "Usage: \n"
+  "                  .\n"
+  "                 /|regen\n"
+  "                / |\n"
+  "            +--(  |------------+\n"
+  "            |   \\ |            |   .\n"
+  "           _V_   \\|  _______   |   |\\ width   ___\n"
+  "          |   |   ' |       |  |   | \\       |   |\n"
+  "      +-->| + |---->| DELAY |--+-->|  )----->|   |\n"
+  "      |   |___|     |_______|      | /       |   |\n"
+  "      |           delay : depth    |/        |   |\n"
+  "  In  |                 : interp   '         |   | Out\n"
+  "  --->+               __:__                  | + |--->\n"
+  "      |              |     |speed            |   |\n"
+  "      |              |  ~  |shape            |   |\n"
+  "      |              |_____|phase            |   |\n"
+  "      +------------------------------------->|   |\n"
+  "                                             |___|\n"
+  "\n"
+  "Usage: flanger [delay depth regen width speed shape phase interp]\n"
+  "\n"
+  "       RANGE DEFAULT DESCRIPTION\n"
+  "delay   0 10    0    base delay in milliseconds\n"
+  "depth   0 10    2    added swept delay in milliseconds\n"
+  "regen -95 +95   0    percentage regeneration (delayed signal feedback)\n"
+  "width   0 100   71   percentage of delayed signal mixed with original\n"
+  "speed  0.1 10  0.5   sweeps per second (Hz) \n"
+  "shape    --    sin   swept wave shape: sine|triangle\n"
+  "phase   0 100   25   swept wave percentage phase-shift for multi-channel\n"
+  "                     (e.g. stereo) flange; 0 = 100 = same phase on each channel\n"
+  "interp   --    lin   delay-line interpolation: linear|quadratic";
+
+/* TODO: Slide in the delay at the start? */
+
+
+
+#include "st_i.h"
 #include <math.h>
 #include <string.h>
-#include "st_i.h"
 
-static st_effect_t st_flanger_effect;
 
-#define MOD_SINE        0
-#define MOD_TRIANGLE    1
 
-/* Private data for SKEL file */
-typedef struct flangerstuff {
-        int     modulation;
-        int     counter;                        
-        int     phase;
-        double  *flangerbuf;
-        float   in_gain, out_gain;
-        float   delay, decay;
-        float   speed;
-        st_size_t length;
-        int     *lookup_tab;
-        st_size_t maxsamples, fade_out;
-} *flanger_t;
+typedef enum {INTERP_LINEAR, INTERP_QUADRATIC} interp_t;
 
-/* Private data for SKEL file */
+#define MAX_CHANNELS 4
 
-/*
- * Process options
- */
-int st_flanger_getopts(eff_t effp, int n, char **argv) 
-{
-        flanger_t flanger = (flanger_t) effp->priv;
 
-        if (!((n == 5) || (n == 6)))
-        {
-            st_fail(st_flanger_effect.usage);
-            return (ST_EOF);
-        }
 
-        sscanf(argv[0], "%f", &flanger->in_gain);
-        sscanf(argv[1], "%f", &flanger->out_gain);
-        sscanf(argv[2], "%f", &flanger->delay);
-        sscanf(argv[3], "%f", &flanger->decay);
-        sscanf(argv[4], "%f", &flanger->speed);
-        flanger->modulation = MOD_SINE;
-        if ( n == 6 ) {
-                if ( !strcmp(argv[5], "-s"))
-                        flanger->modulation = MOD_SINE;
-                else if ( ! strcmp(argv[5], "-t"))
-                        flanger->modulation = MOD_TRIANGLE;
-                else
-                {
-                        st_fail(st_flanger_effect.usage);
-                        return (ST_EOF);
-                }
-        }
-        return (ST_SUCCESS);
+typedef struct flanger {
+  /* Parameters */
+  double     delay_min;
+  double     delay_depth;
+  double     feedback_gain;
+  double     delay_gain;
+  double     speed;
+  st_wave_t  wave_shape;
+  double     channel_phase;
+  interp_t   interpolation;
+            
+  /* Delay buffers */
+  double *   delay_bufs[MAX_CHANNELS];
+  st_size_t  delay_buf_length;
+  st_size_t  delay_buf_pos;
+  double     delay_last[MAX_CHANNELS];
+            
+  /* Low Frequency Oscillator */
+  float *    lfo;
+  st_size_t  lfo_length;
+  st_size_t  lfo_pos;
+            
+  /* Balancing */
+  double     in_gain;
+} * flanger_t;
+
+assert_static(sizeof(struct flanger) <= ST_MAX_EFFECT_PRIVSIZE,
+              /* else */ flanger_PRIVSIZE_too_big);
+
+
+
+static enum_item const interp_enum[] = {
+  ENUM_ITEM(INTERP_,LINEAR)
+  ENUM_ITEM(INTERP_,QUADRATIC)
+  {0}};
+
+
+
+#define NUMERIC_PARAMETER(p, min, max) { \
+  char * end_ptr; \
+  double d; \
+  if (argc == 0) break; \
+  d = strtod(*argv, &end_ptr); \
+  if (end_ptr != *argv) { \
+    if (d < min || d > max || *end_ptr != '\0') { \
+      st_fail(effp->h->usage); \
+      return ST_EOF; \
+    } \
+    f->p = d; \
+    --argc, ++argv; \
+  } \
+}
+
+
+
+#define TEXTUAL_PARAMETER(p, enum_table) { \
+  enum_item const * e; \
+  if (argc == 0) break; \
+  e = find_enum_text(*argv, enum_table); \
+  if (e != NULL) { \
+    f->p = e->value; \
+    --argc, ++argv; \
+  } \
 }
 
-/*
- * Prepare for processing.
- */
-int st_flanger_start(eff_t effp)
+
+
+static int st_flanger_getopts(eff_t effp, int argc, char const * const * argv)
 {
-        flanger_t flanger = (flanger_t) effp->priv;
-        unsigned int i;
+  flanger_t f = (flanger_t) effp->priv;
 
-        flanger->maxsamples = flanger->delay * effp->ininfo.rate / 1000.0;
+  /* Set non-zero defaults: */
+  f->delay_depth  = 2;
+  f->delay_gain   = 71;
+  f->speed        = 0.5;
+  f->channel_phase= 25;
 
-        if ( flanger->in_gain < 0.0 )
-        {
-            st_fail("flanger: gain-in must be positive!");
-            return (ST_EOF);
-        }
-        if ( flanger->in_gain > 1.0 )
-        {
-            st_fail("flanger: gain-in must be less than 1.0!");
-            return (ST_EOF);
-        }
-        if ( flanger->out_gain < 0.0 )
-        {
-            st_fail("flanger: gain-out must be positive!");
-            return (ST_EOF);
-        }
-        if ( flanger->delay < 0.0 )
-        {
-            st_fail("flanger: delay must be positive!");
-            return (ST_EOF);
-        }
-        if ( flanger->delay > 5.0 )
-        {
-            st_fail("flanger: delay must be less than 5.0 msec!");
-            return (ST_EOF);
-        }
-        if ( flanger->speed < 0.1 )
-        {
-            st_fail("flanger: speed must be more than 0.1 Hz!");
-            return (ST_EOF);
-        }
-        if ( flanger->speed > 2.0 )
-        {
-            st_fail("flanger: speed must be less than 2.0 Hz!");
-            return (ST_EOF);
-        }
-        if ( flanger->decay < 0.0 )
-        {
-            st_fail("flanger: decay must be positive!" );
-            return (ST_EOF);
-        }
-        if ( flanger->decay > 1.0 )
-        {
-            st_fail("flanger: decay must be less that 1.0!" );
-            return (ST_EOF);
-        }
-        /* Be nice and check the hint with warning, if... */
-        if ( flanger->in_gain * ( 1.0 + flanger->decay ) > 1.0 / flanger->out_gain )
-                st_warn("flanger: warning >>> gain-out can cause saturation or clipping of output <<<");
+  do { /* break-able block */
+    NUMERIC_PARAMETER(delay_min    , 0  , 10 )
+    NUMERIC_PARAMETER(delay_depth  , 0  , 10 )
+    NUMERIC_PARAMETER(feedback_gain,-95 , 95 )
+    NUMERIC_PARAMETER(delay_gain   , 0  , 100)
+    NUMERIC_PARAMETER(speed        , 0.1, 10 )
+    TEXTUAL_PARAMETER(wave_shape, st_wave_enum)
+    NUMERIC_PARAMETER(channel_phase, 0  , 100)
+    TEXTUAL_PARAMETER(interpolation, interp_enum)
+  } while (0);
 
-        flanger->length = effp->ininfo.rate / flanger->speed;
+  if (argc != 0) {
+    st_fail(effp->h->usage);
+    return ST_EOF;
+  }
 
-        if (! (flanger->flangerbuf = 
-                (double *) malloc(sizeof (double) * flanger->maxsamples)))
-        {
-                st_fail("flanger: Cannot malloc %d bytes!", 
-                        sizeof(double) * flanger->maxsamples);
-                return (ST_EOF);
-        }
-        for ( i = 0; i < flanger->maxsamples; i++ )
-                flanger->flangerbuf[i] = 0.0;
-        if (! (flanger->lookup_tab = 
-                (int *) malloc(sizeof (int) * flanger->length)))
-        {
-                st_fail("flanger: Cannot malloc %d bytes!", 
-                        sizeof(int) * flanger->length);
-                return(ST_EOF);
-        }
+  st_report("parameters:\n"
+      "delay = %gms\n"
+      "depth = %gms\n"
+      "regen = %g%%\n"
+      "width = %g%%\n"
+      "speed = %gHz\n"
+      "shape = %s\n"
+      "phase = %g%%\n"
+      "interp= %s",
+      f->delay_min,
+      f->delay_depth,
+      f->feedback_gain,
+      f->delay_gain,
+      f->speed,
+      st_wave_enum[f->wave_shape].text,
+      f->channel_phase,
+      interp_enum[f->interpolation].text);
 
-        if ( flanger->modulation == MOD_SINE )
-                st_sine(flanger->lookup_tab, flanger->length, 
-                        flanger->maxsamples - 1,
-                        flanger->maxsamples - 1);
-        else
-                st_triangle(flanger->lookup_tab, flanger->length, 
-                        (flanger->maxsamples - 1) * 2, 
-                        flanger->maxsamples - 1);
-        flanger->counter = 0;
-        flanger->phase = 0;
-        flanger->fade_out = flanger->maxsamples;
-        return (ST_SUCCESS);
+  return ST_SUCCESS;
 }
 
-/*
- * Processed signed long samples from ibuf to obuf.
- * Return number of samples processed.
- */
-int st_flanger_flow(eff_t effp, const st_sample_t *ibuf, st_sample_t *obuf, 
-                    st_size_t *isamp, st_size_t *osamp)
+
+
+static int st_flanger_start(eff_t effp)
 {
-        flanger_t flanger = (flanger_t) effp->priv;
-        int len, done;
-        
-        double d_in, d_out;
-        st_sample_t out;
+  flanger_t f = (flanger_t) effp->priv;
+  int c, channels = effp->ininfo.channels;
 
-        len = ((*isamp > *osamp) ? *osamp : *isamp);
-        for(done = 0; done < len; done++) {
-                /* Store delays as 24-bit signed longs */
-                d_in = (double) *ibuf++ / 256;
-                /* Compute output first */
-                d_out = d_in * flanger->in_gain;
-                d_out += flanger->flangerbuf[(flanger->maxsamples + 
-        flanger->counter - flanger->lookup_tab[flanger->phase]) % 
-        flanger->maxsamples] * flanger->decay;
-                /* Adjust the output volume and size to 24 bit */
-                d_out = d_out * flanger->out_gain;
-                out = ST_24BIT_CLIP_COUNT((st_sample_t) d_out, effp->clippedCount);
-                *obuf++ = out * 256;
-                /* Mix decay of delay and input */
-                flanger->flangerbuf[flanger->counter] = d_in;
-                flanger->counter = 
-                        ( flanger->counter + 1 ) % flanger->maxsamples;
-                flanger->phase  = ( flanger->phase + 1 ) % flanger->length;
-        }
-        /* processed all samples */
-        return (ST_SUCCESS);
+  if (channels > MAX_CHANNELS) {
+    st_fail("Can not operate with more than %i channels", MAX_CHANNELS);
+    return ST_EOF;
+  }
+
+  /* Scale percentages to unity: */
+  f->feedback_gain /= 100;
+  f->delay_gain    /= 100;
+  f->channel_phase /= 100;
+
+  /* Balance output: */
+  f->in_gain = 1 / (1 + f->delay_gain);
+  f->delay_gain  /= 1 + f->delay_gain;
+
+  /* Balance feedback loop: */
+  f->delay_gain *= 1 - fabs(f->feedback_gain);
+
+  st_debug("in_gain=%g feedback_gain=%g delay_gain=%g\n",
+      f->in_gain, f->feedback_gain, f->delay_gain);
+
+  /* Create the delay buffers, one for each channel: */
+  f->delay_buf_length =
+    (f->delay_min + f->delay_depth) / 1000 * effp->ininfo.rate + 0.5;
+  ++f->delay_buf_length;  /* Need 0 to n, i.e. n + 1. */
+  ++f->delay_buf_length;  /* Quadratic interpolator needs one more. */
+  for (c = 0; c < channels; ++c) {
+    f->delay_bufs[c] = calloc(f->delay_buf_length, sizeof(*f->delay_bufs[0]));
+    if (f->delay_bufs[c] == NULL)
+    {
+      st_fail("Cannot allocate memory for delay_bufs");
+      return ST_EOF;
+    }
+  }
+
+  /* Create the LFO lookup table: */
+  f->lfo_length = effp->ininfo.rate / f->speed;
+  f->lfo = calloc(f->lfo_length, sizeof(*f->lfo));
+  if (f->lfo == NULL) {
+    st_fail("Cannot allocate memory for lfo");
+    return ST_EOF;
+  }
+  st_generate_wave_table(
+      f->wave_shape,
+      ST_FLOAT,
+      f->lfo,
+      f->lfo_length,
+      (st_size_t)(f->delay_min / 1000 * effp->ininfo.rate + .5),
+      f->delay_buf_length - 2,
+      3 * M_PI_2);  /* Start the sweep at minimum delay (for mono at least) */
+
+  st_debug("delay_buf_length=%u lfo_length=%u\n",
+      f->delay_buf_length, f->lfo_length);
+
+  return ST_SUCCESS;
 }
 
-/*
- * Drain out reverb lines. 
- */
-int st_flanger_drain(eff_t effp, st_sample_t *obuf, st_size_t *osamp)
+
+
+static int st_flanger_flow(eff_t effp, st_sample_t const * ibuf,
+    st_sample_t * obuf, st_size_t * isamp, st_size_t * osamp)
 {
-        flanger_t flanger = (flanger_t) effp->priv;
-        st_size_t done;
-        
-        double d_in, d_out;
-        st_sample_t out;
+  flanger_t f = (flanger_t) effp->priv;
+  int c, channels = effp->ininfo.channels;
+  st_size_t len = (*isamp > *osamp ? *osamp : *isamp) / channels;
 
-        done = 0;
-        while ( ( done < *osamp ) && ( done < flanger->fade_out ) ) {
-                d_in = 0;
-                d_out = 0;
-                /* Compute output first */
-                d_out += flanger->flangerbuf[(flanger->maxsamples + 
-        flanger->counter - flanger->lookup_tab[flanger->phase]) % 
-        flanger->maxsamples] * flanger->decay;
-                /* Adjust the output volume and size to 24 bit */
-                d_out = d_out * flanger->out_gain;
-                out = ST_24BIT_CLIP_COUNT((st_sample_t) d_out, effp->clippedCount);
-                *obuf++ = out * 256;
-                /* Mix decay of delay and input */
-                flanger->flangerbuf[flanger->counter] = d_in;
-                flanger->counter = 
-                        ( flanger->counter + 1 ) % flanger->maxsamples;
-                flanger->phase  = ( flanger->phase + 1 ) % flanger->length;
-                done++;
-                flanger->fade_out--;
-        }
-        /* samples playd, it remains */
-        *osamp = done;
-        return (ST_SUCCESS);
+  *isamp = *osamp = len * channels;
+
+  while (len--) {
+    f->delay_buf_pos =
+      (f->delay_buf_pos + f->delay_buf_length - 1) % f->delay_buf_length;
+    for (c = 0; c < channels; ++c) {
+      double delayed_0, delayed_1;
+      double delayed;
+      double in, out;
+      st_size_t channel_phase = c * f->lfo_length * f->channel_phase + .5;
+      double delay = f->lfo[(f->lfo_pos + channel_phase) % f->lfo_length];
+      double frac_delay = modf(delay, &delay);
+      st_size_t int_delay = (size_t)delay;
+
+      in = *ibuf++;
+      f->delay_bufs[c][f->delay_buf_pos] = in + f->delay_last[c] * f->feedback_gain;
+
+      delayed_0 = f->delay_bufs[c]
+        [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
+      delayed_1 = f->delay_bufs[c]
+        [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
+
+      if (f->interpolation == INTERP_LINEAR)
+        delayed = delayed_0 + (delayed_1 - delayed_0) * frac_delay;
+      else /* if (f->interpolation == INTERP_QUADRATIC) */
+      {
+        double a, b;
+        double delayed_2 = f->delay_bufs[c]
+          [(f->delay_buf_pos + int_delay++) % f->delay_buf_length];
+        delayed_2 -= delayed_0;
+        delayed_1 -= delayed_0;
+        a = delayed_2 *.5 - delayed_1;
+        b = delayed_1 * 2 - delayed_2 *.5;
+        delayed = delayed_0 + (a * frac_delay + b) * frac_delay;
+      }
+
+      f->delay_last[c] = delayed;
+      out = in * f->in_gain + delayed * f->delay_gain;
+      *obuf++ = ST_ROUND_CLIP_COUNT(out, effp->clippedCount);
+    }
+    f->lfo_pos = (f->lfo_pos + 1) % f->lfo_length;
+  }
+
+  return ST_SUCCESS;
 }
 
-/*
- * Clean up flanger effect.
- */
-int st_flanger_stop(eff_t effp)
+
+
+static int st_flanger_stop(eff_t effp)
 {
-        flanger_t flanger = (flanger_t) effp->priv;
+  flanger_t f = (flanger_t) effp->priv;
+  int c, channels = effp->ininfo.channels;
 
-        free((char *) flanger->flangerbuf);
-        flanger->flangerbuf = (double *) -1;   /* guaranteed core dump */
-        free((char *) flanger->lookup_tab);
-        flanger->lookup_tab = (int *) -1;   /* guaranteed core dump */
-        return (ST_SUCCESS);
+  for (c = 0; c < channels; ++c)
+    if (f->delay_bufs[c] != NULL)
+      free(f->delay_bufs[c]);
+
+  if (f->lfo != NULL)
+    free(f->lfo);
+
+  memset(f, 0, sizeof(*f));
+
+  return ST_SUCCESS;
 }
 
+
+
 static st_effect_t st_flanger_effect = {
   "flanger",
-  "Usage: flanger gain-in gain-out delay decay speed [ -s | -t ]",
-  0,
+  st_flanger_usage,
+  ST_EFF_MCHAN,
   st_flanger_getopts,
   st_flanger_start,
   st_flanger_flow,
-  st_flanger_drain,
+  st_effect_nothing_drain,
   st_flanger_stop
 };
 
-const st_effect_t *st_flanger_effect_fn(void)
+
+
+st_effect_t const * st_flanger_effect_fn(void)
 {
-    return &st_flanger_effect;
+  return &st_flanger_effect;
 }
--- a/src/misc.c
+++ b/src/misc.c
@@ -424,38 +424,59 @@
     srand(t);
 }
 
-/* This was very painful.  We need a sine library. */
 
-void st_sine(int *buf, st_size_t len, int max, int depth)
-{
-    st_ssize_t i;
-    int offset;
-    double val;
 
-    offset = max - depth;
-    for (i = 0; i < len; i++) {
-        val = sin((double)i/(double)len * 2.0 * M_PI);
-        buf[i] = (int) ((1.0 + val) * depth / 2.0);
-    }
-}
-
-void st_triangle(int *buf, st_size_t len, int max, int depth)
+void st_generate_wave_table(
+    st_wave_t wave_type,
+    st_data_t data_type,
+    void * table,
+    uint32_t table_size,
+    double min,
+    double max,
+    double phase)
 {
-    st_ssize_t i;
-    int offset;
-    double val;
+  uint32_t t;
+  uint32_t phase_offset = phase / M_PI / 2 * table_size + 0.5;
 
-    offset = max - 2 * depth;
-    for (i = 0; i < len / 2; i++) {
-        val = i * 2.0 / len;
-        buf[i] = offset + (int) (val * 2.0 * (double)depth);
+  for (t = 0; t < table_size; t++)
+  {
+    uint32_t point = (t + phase_offset) % table_size;
+    double d;
+    switch (wave_type)
+    {
+      case ST_WAVE_SINE:
+      d = (sin((double)point / table_size * 2 * M_PI) + 1) / 2;
+      break;
+
+      case ST_WAVE_TRIANGLE:
+      d = (double)point * 2 / table_size;
+      switch (4 * point / table_size)
+      {
+        case 0:         d = d + 0.5; break;
+        case 1: case 2: d = 1.5 - d; break;
+        case 3:         d = d - 1.5; break;
+      }
+      break;
     }
-    for (i = len / 2; i < len ; i++) {
-        val = (len - i) * 2.0 / len;
-        buf[i] = offset + (int) (val * 2.0 * (double)depth);
+    d  = d * (max - min) + min;
+    switch (data_type)
+    {
+      case ST_FLOAT : *(float  *)table = d; table += sizeof(float ); continue;
+      case ST_DOUBLE: *(double *)table = d; table += sizeof(double); continue;
+      default: break;
     }
+    d += d < 0? -0.5 : +0.5;
+    switch (data_type)
+    {
+      case ST_SHORT : *(short  *)table = d; table += sizeof(short ); continue;
+      case ST_INT   : *(int    *)table = d; table += sizeof(int   ); continue;
+      default: break;
+    }
+  }
 }
 
+
+
 const char *st_version(void)
 {
   return PACKAGE_VERSION;
@@ -500,3 +521,26 @@
 
     return ft->st_errno;
 }
+
+enum_item const * find_enum_text(char const * text, enum_item const * enum_items)
+{
+  enum_item const * result = NULL; /* Assume not found */
+
+  while (enum_items->text)
+  {
+    if (strncasecmp(text, enum_items->text, strlen(text)) == 0)
+    {
+      if (result != NULL && result->value != enum_items->value)
+        return NULL;        /* Found ambiguity */
+      result = enum_items;  /* Found match */
+    }
+    ++enum_items;
+  }
+  return result;
+}
+
+enum_item const st_wave_enum[] = {
+  ENUM_ITEM(ST_WAVE_,SINE)
+  ENUM_ITEM(ST_WAVE_,TRIANGLE)
+  {0}};
+
--- a/src/phaser.c
+++ b/src/phaser.c
@@ -176,14 +176,12 @@
                 return (ST_EOF);
         }
 
-        if ( phaser->modulation == MOD_SINE )
-                st_sine(phaser->lookup_tab, phaser->length, 
-                        phaser->maxsamples - 1,
-                        phaser->maxsamples - 1);
+        if (phaser->modulation == MOD_SINE)
+          st_generate_wave_table(ST_WAVE_SINE, ST_INT, phaser->lookup_tab,
+              phaser->length, 0, phaser->maxsamples - 1, 0);
         else
-                st_triangle(phaser->lookup_tab, phaser->length, 
-                        (phaser->maxsamples - 1) * 2,
-                        phaser->maxsamples - 1);
+          st_generate_wave_table(ST_WAVE_TRIANGLE, ST_INT, phaser->lookup_tab,
+              phaser->length, 0, 2 * (phaser->maxsamples - 1), 3 * M_PI_2);
         phaser->counter = 0;
         phaser->phase = 0;
         phaser->fade_out = phaser->maxsamples;
--- a/src/skeleff.c
+++ b/src/skeleff.c
@@ -3,7 +3,7 @@
  *
  * Written by Chris Bagwell (cbagwell@sprynet.com) - March 16, 1999
  *
-  * Copyright 1999 Chris Bagwell And Sundry Contributors
+ * Copyright 1999 Chris Bagwell And Sundry Contributors
  * This source code is freely redistributable and may be used for
  * any purpose.  This copyright notice must be maintained. 
  * Chris Bagwell And Sundry Contributors are not responsible for 
@@ -13,10 +13,13 @@
 
 #include "st_i.h"
 
-/* Private data for SKEL file */
+/* Private data for effect */
 typedef struct skelleffstuff {
         int  localdata;
 } *skeleff_t;
+
+assert_static(sizeof(struct skelleffstuff) <= ST_MAX_EFFECT_PRIVSIZE, 
+    /* else */ skelleff_PRIVSIZE_too_big);
 
 /*
  * Process options
--- a/src/st_i.h
+++ b/src/st_i.h
@@ -34,8 +34,22 @@
 #endif
 
 /* declared in misc.c */
-void st_sine(int *buf, st_size_t len, int max, int depth);
-void st_triangle(int *buf, st_size_t len, int max, int depth);
+typedef struct {char const *text; int value;} enum_item;
+#define ENUM_ITEM(prefix, item) {#item, prefix##item},
+enum_item const * find_enum_text(
+    char const * text, enum_item const * enum_items);
+
+typedef enum {ST_SHORT, ST_INT, ST_FLOAT, ST_DOUBLE} st_data_t;
+typedef enum {ST_WAVE_SINE, ST_WAVE_TRIANGLE} st_wave_t;
+extern enum_item const st_wave_enum[];
+void st_generate_wave_table(
+    st_wave_t wave_type,
+    st_data_t data_type,
+    void * table,
+    uint32_t table_size,
+    double min,
+    double max,
+    double phase);
 
 st_sample_t st_gcd(st_sample_t a, st_sample_t b) REGPARM(2);
 st_sample_t st_lcm(st_sample_t a, st_sample_t b) REGPARM(2);
--- a/src/synth.c
+++ b/src/synth.c
@@ -16,26 +16,6 @@
 #include <ctype.h>
 #include "st_i.h"
 
-typedef struct {char const *text; int value;} enum_item;
-#define ENUM_ITEM(prefix, item) {#item, prefix##item},
-
-static enum_item const * find(char const * text, enum_item const * enum_items)
-{
-  enum_item const * result = NULL; /* Assume not found */
-
-  while (enum_items->text)
-  {
-    if (strncasecmp(text, enum_items->text, strlen(text)) == 0)
-    {
-      if (result != NULL && result->value != enum_items->value)
-        return NULL;        /* Found ambiguity */
-      result = enum_items;  /* Found match */
-    }
-    ++enum_items;
-  }
-  return result;
-}
-
 static st_effect_t st_synth_effect;
 
 #define PCOUNT 5
@@ -315,7 +295,7 @@
     /* for one or more channel */
     /* type [combine] [f1[-f2]] [p0] [p1] [p2] [p3] [p4] */
     for (c = 0; c < MAXCHAN && n > argn; c++) {
-      enum_item const * p = find(argv[argn], synth_type);
+      enum_item const * p = find_enum_text(argv[argn], synth_type);
       if (p == NULL) {
         st_fail("no type given");
         return ST_EOF;
@@ -324,7 +304,7 @@
       if (++argn == n) break;
 
       /* maybe there is a combine-type in next arg */
-      p = find(argv[argn], combine_type);
+      p = find_enum_text(argv[argn], combine_type);
       if (p != NULL) {
         synth->mix[c] = p->value;
         if (++argn == n) break;
--- a/src/vibro.c
+++ b/src/vibro.c
@@ -63,22 +63,6 @@
         return (ST_SUCCESS);
 }
 
-/* This was very painful.  We need a sine library. */
-/* SJB: this is somewhat different than st_sine()  */
-/* FIXME: move to misc.c */
-static void sine(short *buf, int len, float depth)
-{
-        int i;
-        int scale = depth * 128;
-        int base = (1.0 - depth) * 128;
-        double val;
-
-        for (i = 0; i < len; i++) {
-                val = sin((float)i/(float)len * 2.0 * M_PI);
-                buf[i] = (val + 1.0) * scale + base * 2;
-        }
-}
-
 /*
  * Prepare processing.
  */
@@ -94,7 +78,8 @@
                 return (ST_EOF);
         }
 
-        sine(vibro->sinetab, vibro->length, vibro->depth);
+        st_generate_wave_table(ST_WAVE_SINE, ST_SHORT,
+            vibro->sinetab, vibro->length, (1 - vibro->depth) * 256, 256, 0);
         vibro->counter = 0;
         return (ST_SUCCESS);
 }