shithub: sox

Download patch

ref: 88c8553442a1bfad77eb52aa96ac08837918bda5
parent: d4b408daa75b870e306c91727aca682546634716
author: robs <robs>
date: Sun May 13 02:10:31 EDT 2007

Fixed:
	Brown noise generation.
	Square wave was inverted.
	Negative offsets.
Added:
	More parameter checking.
	Support for arbitrary number of channels.
Cleaned up:
	Lots.

--- a/src/synth.c
+++ b/src/synth.c
@@ -1,73 +1,61 @@
 /*
- * synth - Synthesizer Effect.  
+ * synth - Synthesizer Effect.
  *
- * Written by Carsten Borchardt Jan 2001
- * Version 0.1
+ * Copyright (c) Jan 2001  Carsten Borchardt
+ * Copyright (c) 2001-2007 SoX contributers
  *
- * This source code is freely redistributable and may be used for
- * any purpose.  This copyright notice must be maintained. 
- * The authors are not responsible for 
- * the consequences of using this software.
+ * This source code is freely redistributable and may be used for any purpose.
+ * This copyright notice must be maintained.  The authors are not responsible
+ * for the consequences of using this software.
  */
 
-#include <signal.h>
 #include <string.h>
 #include <math.h>
 #include <ctype.h>
-#include "synth.h"
+#include "sox_i.h"
 
-static sox_effect_t sox_synth_effect;
+typedef enum {
+  synth_sine,
+  synth_square,
+  synth_sawtooth,
+  synth_triangle,
+  synth_trapezium,
+  synth_trapetz  = synth_trapezium,   /* Deprecated name for trapezium */
+  synth_exp,
+                                      /* Repetetives above, noises below */
+  synth_whitenoise,
+  synth_noise = synth_whitenoise,     /* Just a handy alias */
+  synth_pinknoise,
+  synth_brownnoise
+} type_t;
 
-#define PCOUNT 5
-
-#define SYNTH_SINE       0
-#define SYNTH_SQUARE     1
-#define SYNTH_SAWTOOTH   2
-#define SYNTH_TRIANGLE   3
-#define SYNTH_TRAPEZIUM  4
-#define SYNTH_TRAPETZ    SYNTH_TRAPEZIUM /* Deprecated name for trapezium */
-#define SYNTH_WHITENOISE 5
-#define SYNTH_NOISE      SYNTH_WHITENOISE /* Just a handy alias */
-#define SYNTH_PINKNOISE  6
-#define SYNTH_BROWNNOISE 7
-#define SYNTH_EXP        8
-
-#define SYNTH_CREATE    0x000
-#define SYNTH_MIX       0x100
-#define SYNTH_AMOD      0x200
-#define SYNTH_FMOD      0x400
-
 enum_item const synth_type[] = {
-  ENUM_ITEM(SYNTH_,SINE      )
-  ENUM_ITEM(SYNTH_,SQUARE    )
-  ENUM_ITEM(SYNTH_,SAWTOOTH  )
-  ENUM_ITEM(SYNTH_,TRIANGLE  )
-  ENUM_ITEM(SYNTH_,TRAPEZIUM )
-  ENUM_ITEM(SYNTH_,TRAPETZ   )
-  ENUM_ITEM(SYNTH_,WHITENOISE)
-  ENUM_ITEM(SYNTH_,NOISE     )
-  ENUM_ITEM(SYNTH_,PINKNOISE )
-  ENUM_ITEM(SYNTH_,BROWNNOISE)
-  ENUM_ITEM(SYNTH_,EXP       )
-  {0, 0}};
+  ENUM_ITEM(synth_, sine)
+  ENUM_ITEM(synth_, square)
+  ENUM_ITEM(synth_, sawtooth)
+  ENUM_ITEM(synth_, triangle)
+  ENUM_ITEM(synth_, trapezium)
+  ENUM_ITEM(synth_, trapetz)
+  ENUM_ITEM(synth_, exp)
+  ENUM_ITEM(synth_, whitenoise)
+  ENUM_ITEM(synth_, noise)
+  ENUM_ITEM(synth_, pinknoise)
+  ENUM_ITEM(synth_, brownnoise)
+  {0, 0}
+};
 
+typedef enum {synth_create, synth_mix, synth_amod, synth_fmod} combine_t;
+
 enum_item const combine_type[] = {
-  ENUM_ITEM(SYNTH_,CREATE)
-  ENUM_ITEM(SYNTH_,MIX   )
-  ENUM_ITEM(SYNTH_,AMOD  )
-  ENUM_ITEM(SYNTH_,FMOD  )
-  {0, 0}};
+  ENUM_ITEM(synth_, create)
+  ENUM_ITEM(synth_, mix)
+  ENUM_ITEM(synth_, amod)
+  ENUM_ITEM(synth_, fmod)
+  {0, 0}
+};
 
-/* do not ask me for the colored noise, i copied the 
- * algorithm somewhere...
- */
-#define BROWNNOISE_FAC  (500.0/32768.0)
-#define PINKNOISE_FAC   (5000.0/32768.0)
-#define LOG_10_20     0.1151292546497022842009e0
 
-#define MAXCHAN 4
 
-
 /******************************************************************************
  * start of pink noise generator stuff
  * algorithm stolen from:
@@ -74,13 +62,13 @@
  * Author: Phil Burk, http://www.softsynth.com
  */
 
-  
 /* Calculate pseudo-random 32 bit number based on linear congruential method. */
-static unsigned long GenerateRandomNumber( void )
+static unsigned long GenerateRandomNumber(void)
 {
-        static unsigned long randSeed = 22222;  /* Change this for different random sequences. */
-        randSeed = (randSeed * 196314165) + 907633515;
-        return randSeed;
+  static unsigned long randSeed = 22222;        /* Change this for different random sequences. */
+
+  randSeed = (randSeed * 196314165) + 907633515;
+  return randSeed;
 }
 
 #define PINK_MAX_RANDOM_ROWS   (30)
@@ -87,70 +75,71 @@
 #define PINK_RANDOM_BITS       (24)
 #define PINK_RANDOM_SHIFT      ((sizeof(long)*8)-PINK_RANDOM_BITS)
 
-typedef struct{
-    long      pink_Rows[PINK_MAX_RANDOM_ROWS];
-    long      pink_RunningSum;   /* Used to optimize summing of generators. */
-    int       pink_Index;        /* Incremented each sample. */
-    int       pink_IndexMask;    /* Index wrapped by ANDing with this mask. */
-    float     pink_Scalar;       /* Used to scale within range of -1.0 to +1.0 */
+typedef struct {
+  long pink_Rows[PINK_MAX_RANDOM_ROWS];
+  long pink_RunningSum;         /* Used to optimize summing of generators. */
+  int pink_Index;               /* Incremented each sample. */
+  int pink_IndexMask;           /* Index wrapped by ANDing with this mask. */
+  float pink_Scalar;            /* Used to scale within range of -1 to +1 */
 } PinkNoise;
 
 /* Setup PinkNoise structure for N rows of generators. */
-static void InitializePinkNoise( PinkNoise *pink, int numRows )
+static void InitializePinkNoise(PinkNoise * pink, int numRows)
 {
-        int i;
-        long pmax;
-        pink->pink_Index = 0;
-        pink->pink_IndexMask = (1<<numRows) - 1;
-/* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
-        pmax = (numRows + 1) * (1<<(PINK_RANDOM_BITS-1));
-        pink->pink_Scalar = 1.0f / pmax;
-/* Initialize rows. */
-        for( i=0; i<numRows; i++ ) pink->pink_Rows[i] = 0;
-        pink->pink_RunningSum = 0;
+  int i;
+  long pmax;
+
+  pink->pink_Index = 0;
+  pink->pink_IndexMask = (1 << numRows) - 1;
+  /* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
+  pmax = (numRows + 1) * (1 << (PINK_RANDOM_BITS - 1));
+  pink->pink_Scalar = 1.0f / pmax;
+  /* Initialize rows. */
+  for (i = 0; i < numRows; i++)
+    pink->pink_Rows[i] = 0;
+  pink->pink_RunningSum = 0;
 }
 
-/* Generate Pink noise values between -1.0 and +1.0 */
-static float GeneratePinkNoise( PinkNoise *pink )
+/* Generate Pink noise values between -1 and +1 */
+static float GeneratePinkNoise(PinkNoise * pink)
 {
-        long newRandom;
-        long sum;
-        float output;
+  long newRandom;
+  long sum;
+  float output;
 
-/* Increment and mask index. */
-        pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
+  /* Increment and mask index. */
+  pink->pink_Index = (pink->pink_Index + 1) & pink->pink_IndexMask;
 
-/* If index is zero, don't update any random values. */
-        if( pink->pink_Index != 0 )
-        {
-        /* Determine how many trailing zeros in PinkIndex. */
-        /* This algorithm will hang if n==0 so test first. */
-                int numZeros = 0;
-                int n = pink->pink_Index;
-                while( (n & 1) == 0 )
-                {
-                        n = n >> 1;
-                        numZeros++;
-                }
+  /* If index is zero, don't update any random values. */
+  if (pink->pink_Index != 0) {
+    /* Determine how many trailing zeros in PinkIndex. */
+    /* This algorithm will hang if n==0 so test first. */
+    int numZeros = 0;
+    int n = pink->pink_Index;
 
-        /* Replace the indexed ROWS random value.
-         * Subtract and add back to RunningSum instead of adding all the random
-         * values together. Only one changes each time.
-         */
-                pink->pink_RunningSum -= pink->pink_Rows[numZeros];
-                newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
-                pink->pink_RunningSum += newRandom;
-                pink->pink_Rows[numZeros] = newRandom;
-        }
-        
-/* Add extra white noise value. */
-        newRandom = ((long)GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
-        sum = pink->pink_RunningSum + newRandom;
+    while ((n & 1) == 0) {
+      n = n >> 1;
+      numZeros++;
+    }
 
-/* Scale to range of -1.0 to 0.9999. */
-        output = pink->pink_Scalar * sum;
+    /* Replace the indexed ROWS random value.
+     * Subtract and add back to RunningSum instead of adding all the random
+     * values together. Only one changes each time.
+     */
+    pink->pink_RunningSum -= pink->pink_Rows[numZeros];
+    newRandom = ((long) GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+    pink->pink_RunningSum += newRandom;
+    pink->pink_Rows[numZeros] = newRandom;
+  }
 
-        return output;
+  /* Add extra white noise value. */
+  newRandom = ((long) GenerateRandomNumber()) >> PINK_RANDOM_SHIFT;
+  sum = pink->pink_RunningSum + newRandom;
+
+  /* Scale to range of -1 to 0.9999. */
+  output = pink->pink_Scalar * sum;
+
+  return output;
 }
 
 /**************** end of pink noise stuff */
@@ -157,556 +146,455 @@
 
 
 
+typedef struct {
+  /* options */
+  type_t type;
+  combine_t combine;
+  double freq, freq2;
+  double offset, phase;
+  double p1, p2, p3; /* Use depends on synth type */
+
+  /* internal stuff */
+  double cycle_start_time_s;
+  double brown_noise;
+  PinkNoise pink_noise;
+} * channel_t;
+
+
+
 /* Private data for the synthesizer */
-typedef struct synthstuff {
-    /* options */
-    char *length_str;
-    int type[MAXCHAN];
-    int mix[MAXCHAN];
-    double freq[MAXCHAN];
-    double freq2[MAXCHAN];
-    double par[MAXCHAN][5];
+typedef struct {
+  char *        length_str;
+  sox_ssample_t max;
+  sox_size_t    samples_done;
+  sox_size_t    samples_to_do;
+  channel_t     channels;
+  unsigned number_of_channels;
+} * synth_t;
 
-    /* internal stuff */
-    sox_ssample_t max;
-    sox_size_t samples_done;
-    int rate;
-    sox_size_t length; /* length in number of samples */
-    double h[MAXCHAN]; /* store values necessary for  creation */
-    PinkNoise pinkn[MAXCHAN];
-} *synth_t;
 
 
 /* a note is given as an int,
  * 0   => 440 Hz = A
- * >0  => number of half notes 'up', 
+ * >0  => number of half notes 'up',
  * <0  => number of half notes down,
  * example 12 => A of next octave, 880Hz
  *
  * calculated by freq = 440Hz * 2**(note/12)
  */
-static double calc_note_freq(double note){
-    return (440.0 * pow(2.0,note/12.0));
+static double calc_note_freq(double note)
+{
+  return 440.0 * pow(2.0, note / 12.0);
 }
 
 
-/* read string 's' and convert to frequency
- * 's' can be a positive number which is the frequency in Hz
- * if 's' starts with a hash '%' and a following number the corresponding
- * note is calculated
- * return -1 on error
- */ 
-static double StringToFreq(char *s, char **h){
-    double f;
 
-    if(*s=='%'){
-        f = strtod(s+1,h);
-        if ( *h == s+1 ){ 
-            /* error*/
-            return -1.0;
-        }
-        f=calc_note_freq(f);
-    }else{
-        f=strtod(s,h);
-        if(*h==s){
-            return -1.0;
-        }
-    }
-    if( f < 0.0 )
-        return -1.0;
-    return f;
+/* Read string 's' and convert to frequency.
+ * 's' can be a positive number which is the frequency in Hz.
+ * If 's' starts with a hash '%' and a following number the corresponding
+ * note is calculated.
+ * Return -1 on error.
+ */
+static double StringToFreq(char *s, char **h)
+{
+  double f;
+
+  if (*s == '%') {
+    f = strtod(s + 1, h);
+    if (*h == s + 1)
+      return -1;
+    f = calc_note_freq(f);
+  } else {
+    f = strtod(s, h);
+    if (*h == s)
+      return -1;
+  }
+  if (f < 0)
+    return -1;
+  return f;
 }
 
 
 
-static void parmcopy(synth_t sy, int s, int d){
-    int i;
-    sy->freq[d]=sy->freq[s];
-    sy->freq2[d]=sy->freq2[s];
-    sy->type[d]=sy->type[s];
-    sy->mix[d]=sy->mix[s];
-    for(i=0;i<PCOUNT;i++){
-        sy->par[d][i]=sy->par[s][i];
-    }
+static void create_channel(channel_t chan)
+{
+  memset(chan, 0, sizeof(*chan));
+  chan->freq2 = chan->freq = 440;
+  chan->p3 = chan->p2 = chan->p1 = -1;
 }
 
 
-/*
- * Process options
- *
- * Don't do initialization now.
- * The 'info' fields are not yet filled in.
- */
-int sox_synth_getopts(eff_t effp, int n, char **argv) 
+
+static void set_default_parameters(channel_t chan, int c)
 {
-    int argn;
-    char *hlp;
-    int i;
-    int c;
-    synth_t synth = (synth_t) effp->priv;
-    
+  switch (chan->type) {
+    case synth_square:    /* p1 is pulse width */
+      if (chan->p1 < 0)
+        chan->p1 = 0.5;   /* default to 50% duty cycle */
+      break;
 
-    /* set default parameters */
-    synth->length = 0; /* use length of input file */
-    synth->length_str = 0;
-    for(c=0;c<MAXCHAN;c++){
-        synth->freq[c] = 440.0;
-        synth->freq2[c] = 440.0;
-        synth->type[c]=SYNTH_SINE; 
-        synth->mix[c] = SYNTH_CREATE;
-    
-        for(i=0;i<PCOUNT;i++)
-            synth->par[c][i]= -1.0;
-        
-        synth->par[c][0]= 0.0; /* offset */
-        synth->par[c][1]= 0.0; /* phase */;
+    case synth_triangle:  /* p1 is position of maximum */
+      if (chan->p1 < 0)
+        chan->p1 = 0.5;
+      break;
+
+    case synth_trapezium:
+      /* p1 is length of rising slope,
+       * p2 position where falling slope begins
+       * p3 position of end of falling slope
+       */
+      if (chan->p1 < 0) {
+        chan->p1 = 0.1;
+        chan->p2 = 0.5;
+        chan->p3 = 0.6;
+      } else if (chan->p2 < 0) { /* try a symetric waveform */
+        if (chan->p1 <= 0.5) {
+          chan->p2 = (1 - 2 * chan->p1) / 2;
+          chan->p3 = chan->p2 + chan->p1;
+        } else {
+          /* symetric is not possible, fall back to asymetrical triangle */
+          chan->p2 = chan->p1;
+          chan->p3 = 1;
+        }
+      } else if (chan->p3 < 0)
+        chan->p3 = 1;     /* simple falling slope to the end */
+      break;
+
+    case synth_pinknoise:
+      /* Initialize pink noise signals with different numbers of rows. */
+      InitializePinkNoise(&(chan->pink_noise), 10 + 2 * c);
+      break;
+
+    case synth_exp:
+      if (chan->p1 < 0) /* p1 is position of maximum */
+        chan->p1 = 0.5;
+      if (chan->p2 < 0) /* p2 is amplitude */
+        chan->p2 = 1;
+      break;
+
+    default: break;
+  }
+}
+
+
+
+static int getopts(eff_t effp, int argc, char **argv)
+{
+  synth_t synth = (synth_t) effp->priv;
+  int argn = 0;
+
+  /* Get duration if given (if first arg starts with digit) */
+  if (argc && (isdigit(argv[argn][0]) || argv[argn][0] == '.')) {
+    synth->length_str = xmalloc(strlen(argv[argn]) + 1);
+    strcpy(synth->length_str, argv[argn]);
+    /* Do a dummy parse of to see if it will fail */
+    if (sox_parsesamples(0, synth->length_str, &synth->samples_to_do, 't') == NULL) {
+      sox_fail(effp->h->usage);
+      return SOX_EOF;
     }
+    argn++;
+  }
 
-    argn=0;
-    if ( n<0){
-        sox_fail(sox_synth_effect.usage);
-        return(SOX_EOF);
+  while (argn < argc) { /* type [combine] [f1[-f2] [p1 [p2 [p3 [p3 [p4]]]]]] */
+    channel_t chan;
+    char * char_ptr;
+    enum_item const *p = find_enum_text(argv[argn], synth_type);
+
+    if (p == NULL) {
+      sox_fail("no type given");
+      return SOX_EOF;
     }
-    if(n==0){
-        /* no arg, use default*/
-        return(SOX_SUCCESS);
-    }
-    
+    synth->channels = xrealloc(synth->channels, sizeof(*synth->channels) * (synth->number_of_channels + 1));
+    chan = &synth->channels[synth->number_of_channels++];
+    create_channel(chan);
+    chan->type = p->value;
+    if (++argn == argc)
+      break;
 
-    /* read length if given ( if first par starts with digit )*/
-    if( isdigit((int)argv[argn][0]) || argv[argn][0] == '.') {
-        synth->length_str = (char *)xmalloc(strlen(argv[argn])+1);
-        strcpy(synth->length_str,argv[argn]);
-        /* Do a dummy parse of to see if it will fail */
-        if (sox_parsesamples(0, synth->length_str, &synth->length, 't') == NULL)
-        {
-            sox_fail(sox_synth_effect.usage);
-            return (SOX_EOF);
-        }
-        argn++;
+    /* maybe there is a combine-type in next arg */
+    p = find_enum_text(argv[argn], combine_type);
+    if (p != NULL) {
+      chan->combine = p->value;
+      if (++argn == argc)
+        break;
     }
-    /* 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_enum_text(argv[argn], synth_type);
-      if (p == NULL) {
-        sox_fail("no type given");
+
+    /* read frequencies if given */
+    if (isdigit((int) argv[argn][0]) || argv[argn][0] == '%') {
+      chan->freq2 = chan->freq = StringToFreq(argv[argn], &char_ptr);
+      if (chan->freq < 0) {
+        sox_fail("invalid freq");
         return SOX_EOF;
       }
-      synth->type[c] = p->value;
-      if (++argn == n) break;
+      if (*char_ptr == '-') {        /* freq2 given? */
+        char *hlp2;
 
-      /* maybe there is a combine-type in next arg */
-      p = find_enum_text(argv[argn], combine_type);
-      if (p != NULL) {
-        synth->mix[c] = p->value;
-        if (++argn == n) break;
-      }
-
-      /* read frequencies if given */
-      if (isdigit((int)argv[argn][0]) || argv[argn][0] == '%') {
-        synth->freq2[c] = synth->freq[c] = StringToFreq(argv[argn], &hlp);
-        if (synth->freq[c] < 0) {
-          sox_fail("invalid freq");
+        chan->freq2 = StringToFreq(char_ptr + 1, &hlp2);
+        if (chan->freq2 < 0) {
+          sox_fail("invalid freq2");
           return SOX_EOF;
         }
-        if (*hlp == '-') { /* freq2 given? */
-          char * hlp2;
-          synth->freq2[c] = StringToFreq(hlp + 1, &hlp2);
-          if (synth->freq2[c] < 0) {
-            sox_fail("invalid freq2");
-            return SOX_EOF;
-          }
-          if (synth->length_str == NULL) {
-            sox_fail("length must be given when using freq2");
-            return SOX_EOF;
-          }
-        }
-        if (++argn == n) break;
-      }
-
-      /* read rest of parameters */
-      for (i = 0; argn < n && isdigit((int)argv[argn][0]); ++i, ++argn) {
-        if (i == PCOUNT) {
-          sox_fail("too many parameters");
+        if (synth->length_str == NULL) {
+          sox_fail("duration must be given when using freq2");
           return SOX_EOF;
         }
-        synth->par[c][i] = strtod(argv[argn], &hlp);
-        if (hlp == argv[argn]) {
-          sox_fail("parameter error");
-          return SOX_EOF;
-        }
       }
-      if (argn == n) break;
+      if (++argn == argc)
+        break;
     }
 
-    /* make some intelligent parameter initialization for channels
-     * where no parameters were given
-     *
-     * - if only parms for one channel were given, copy to other channels
-     * - if parm for 2 channels were given, copy to channel 1->3, 2->4
-     * - if parm for 3 channels were given, copy 2->4
-     */
-    if(c == 0 || c >= MAXCHAN){
-        for(c=1;c<MAXCHAN;c++)
-            parmcopy(synth,0,c);
-    }else if(c == 1){
-        parmcopy(synth,0,2);
-        parmcopy(synth,1,3);
-    }else if(c == 2){
-        parmcopy(synth,1,3);
+    /* read rest of parameters */
+    #define NUMERIC_PARAMETER(p, min, max) { \
+      char * end_ptr; \
+      double d = strtod(argv[argn], &end_ptr); \
+      if (end_ptr == argv[argn]) \
+        break; \
+      if (d < min || d > max || *end_ptr != '\0') { \
+        sox_fail("parameter error"); \
+        return SOX_EOF; \
+      } \
+      chan->p = d / 100; /* adjust so abs(parameter) <= 1 */\
+      if (++argn == argc) \
+        break; \
     }
-
-    return (SOX_SUCCESS);
+    do { /* break-able block */
+      NUMERIC_PARAMETER(offset,-100, 100)
+      NUMERIC_PARAMETER(phase ,   0, 100)
+      NUMERIC_PARAMETER(p1,   0, 100)
+      NUMERIC_PARAMETER(p2,   0, 100)
+      NUMERIC_PARAMETER(p1,   0, 100)
+    } while (0);
+  }
+  return SOX_SUCCESS;
 }
 
 
-/*
- * Prepare processing.
- * Do all initializations.
- */
-int sox_synth_start(eff_t effp)
+
+static int start(eff_t effp)
 {
-    int i;
-    int c;
-    synth_t synth = (synth_t) effp->priv;
-    int shift_for_max = (4 - min(effp->outinfo.size, 4)) << 3;
+  synth_t synth = (synth_t) effp->priv;
+  unsigned i;
+  int shift_for_max = (4 - min(effp->outinfo.size, 4)) << 3;
 
-    synth->max = (SOX_SAMPLE_MAX >> shift_for_max) << shift_for_max;
+  synth->max = (SOX_SAMPLE_MAX >> shift_for_max) << shift_for_max;
+  synth->samples_done = 0;
 
-    if (synth->length_str)
-    {
-        if (sox_parsesamples(effp->ininfo.rate, synth->length_str,
-                            &synth->length, 't') == NULL)
-        {
-            sox_fail(sox_synth_effect.usage);
-            return(SOX_EOF);
-        }
+  if (synth->length_str)
+    if (sox_parsesamples(effp->ininfo.rate, synth->length_str, &synth->samples_to_do, 't') == NULL) {
+      sox_fail(effp->h->usage);
+      return SOX_EOF;
     }
 
-    synth->samples_done=0;
-    synth->rate = effp->ininfo.rate;
-    
-    for(i=0;i< MAXCHAN; i++){
-        synth->h[i]=0.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++]);
+  }
 
-    /* parameter adjustment for all channels */
-    for(c=0;c<MAXCHAN;c++){
-        /* adjust parameter 0 - 100% to 0..1 */
-        for(i=0;i<PCOUNT;i++){
-            synth->par[c][i] /= 100.0;
-        }
-    
-        /* give parameters nice defaults for the different 'type' */
-    
-        switch(synth->type[c]){
-            case SYNTH_SINE:
-                break;
-            case SYNTH_SQUARE:
-                /* p2 is pulse width */
-                if(synth->par[c][2] < 0.0){
-                    synth->par[c][2] = 0.5; /* default to 50% duty cycle */
-                }
-                break;
-            case SYNTH_TRIANGLE:
-                /* p2 is position of maximum*/
-                if(synth->par[c][2] < 0.0){
-                    /* default : 0 */
-                    synth->par[c][2]=0.5;
-                }
-                break;
-            case SYNTH_SAWTOOTH:
-                /* no parameters, use TRIANGLE to create no-default-sawtooth */
-                break;
-            case SYNTH_TRAPETZ:
-                /* p2 is length of rising slope,
-                 * p3 position where falling slope begins
-                 * p4 position of end of falling slope
-                 */
-                if(synth->par[c][2] < 0.0 ){
-                    synth->par[c][2]= 0.1;
-                    synth->par[c][3]= 0.5;
-                    synth->par[c][4]= 0.6;
-                }else if(synth->par[c][3] < 0.0){
-                    /* try a symetric waveform
-                     */
-                    if(synth->par[c][2] <= 0.5){
-                        synth->par[c][3] = (1.0-2.0*synth->par[c][2])/2.0;
-                        synth->par[c][4] = synth->par[c][3] + synth->par[c][2];
-                    }else{
-                        /* symetric is not possible, fall back to asymetrical 
-                         * triangle
-                         */
-                        synth->par[c][3]=synth->par[c][2];
-                        synth->par[c][4]=1.0;
-                    }
-                }else if(synth->par[c][4] < 0.0){
-                    /* simple falling slope to the end */
-                    synth->par[c][4]=1.0;
-                }
-                break;
-            case SYNTH_PINKNOISE:
-                /* Initialize pink noise signals with different numbers of rows. */
-                InitializePinkNoise( &(synth->pinkn[c]),10+2*c);
-                break;
-            case SYNTH_EXP:
-                /* p2 is position of maximum*/
-                if (synth->par[c][2] < 0)
-                  synth->par[c][2] = 0.5;
+  /* 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;
+  }
 
-                /* p2 is amplitude */
-                if (synth->par[c][3] < 0)
-                  synth->par[c][3] = 1;
-                break;
+  for (i = 0; i < synth->number_of_channels; i++)
+    set_default_parameters(&synth->channels[i], i);
 
-            default:
-                break;
-        }
+  for (i = 0; i < effp->ininfo.channels; i++) {
+    channel_t chan = &synth->channels[i];
 
-        sox_debug("type=%i, mix=%i, length=%u, f1=%g, f2=%g",
-                synth->type[c], synth->mix[c], 
-                synth->length, synth->freq[c], synth->freq2[c]);
-        sox_debug("p0=%g, p1=%g, p2=%g, p3=%g, p4=%g",
-                synth->par[c][0], synth->par[c][1],
-                synth->par[c][2], synth->par[c][3], synth->par[c][4]);
-    }
-    sox_debug("inchan=%i, rate=%i", (int)effp->ininfo.channels,synth->rate);
-    return (SOX_SUCCESS);
+    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,
+        find_enum_value(chan->combine, combine_type)->text,
+        synth->samples_to_do, chan->freq, chan->freq2,
+        chan->offset, chan->phase, chan->p1, chan->p2, chan->p3);
+  }
+  return SOX_SUCCESS;
 }
 
 
 
-static sox_ssample_t do_synth(sox_ssample_t iv, synth_t synth, int c){
-    sox_ssample_t ov=iv;
-    double r=0.0; /* -1 .. +1 */
-    double f;
-    double om;
-    double sd;
-    double move;
-    double t,dt ;
+static sox_ssample_t do_synth(sox_ssample_t synth_input, synth_t synth, int c, double rate)
+{
+  channel_t chan = &synth->channels[c];
+  double synth_out;              /* [-1, 1] */
 
-    if(synth->length<=0){
-        /* there is no way to change the freq. without knowing the length
-         * use startfreq all the time ...
-         */
-        f = synth->freq[c];
-    }else{
-        f = synth->freq[c] * 
-            exp( (log(synth->freq2[c])-log(synth->freq[c]))* 
-                 synth->samples_done/synth->length );
+  if (chan->type < synth_noise) { /* Need to calculate phase: */
+    double f;              /* Current frequency; variable if sweeping */
+    double cycle_period_s; /* Current period in seconds */
+    double total_elapsed_time_s, cycle_elapsed_time_s;
+    double phase;            /* [0, 1) */
+
+    if (synth->samples_to_do <= 0)
+      f = chan->freq;      /* Can't sweep if synth duration is unknown */
+    else
+      f = chan->freq * exp((log(chan->freq2) - log(chan->freq)) *
+          synth->samples_done / synth->samples_to_do);
+    cycle_period_s = 1 / f;
+    total_elapsed_time_s = synth->samples_done / rate;
+    cycle_elapsed_time_s = total_elapsed_time_s - chan->cycle_start_time_s;
+    if (cycle_elapsed_time_s >= cycle_period_s) {  /* move to next cycle */
+      chan->cycle_start_time_s += cycle_period_s;
+      cycle_elapsed_time_s = total_elapsed_time_s - chan->cycle_start_time_s;
     }
-    om = 1.0 / f; /* periodendauer inn sec */
-    t = synth->samples_done / (double)synth->rate; /* zeit seit start in sec */
-    dt = t - synth->h[c]; /* seit seitdem letzte periode um war. */
-    if( dt < om){
-        /* wir sind noch in der periode.. */
-    }else{
-        /* schon in naechste periode */
-        synth->h[c]+=om;
-        dt=t-synth->h[c];
-    }
-    sd= dt/om; /* position in der aktuellen periode; 0<= sd < 1*/
-    sd = fmod(sd+synth->par[c][1],1.0); /* phase einbauen */
+    phase = cycle_elapsed_time_s / cycle_period_s;
+    phase = fmod(phase + chan->phase, 1.0);
 
+    switch (chan->type) {
+      case synth_sine:
+        synth_out = sin(2 * M_PI * phase);
+        break;
 
-    switch(synth->type[c]){
-        case SYNTH_SINE:
-            r = sin(2.0 * M_PI * sd);
-            break;
-        case SYNTH_SQUARE:
-            /* |_______           | +1
-             * |       |          |
-             * |_______|__________|  0
-             * |       |          |
-             * |       |__________| -1
-             * |                  |
-             * 0       p2          1
-             */
-            if(sd < synth->par[c][2]){
-                r = -1.0;
-            }else{
-                r = +1.0;
-            }
-            break;
-        case SYNTH_SAWTOOTH:
-            /* |           __| +1
-             * |        __/  |
-             * |_______/_____|  0
-             * |  __/        |
-             * |_/           | -1
-             * |             |
-             * 0             1
-             */
-            r = -1.0 + 2.0 * sd;
-            break;
-        case SYNTH_TRIANGLE:
-            /* |    _    | +1
-             * |   / \   |
-             * |__/___\__|  0
-             * | /     \ |
-             * |/       \| -1
-             * |         |
-             * 0   p2    1
-             */
+      case synth_square:
+        /* |_______           | +1
+         * |       |          |
+         * |_______|__________|  0
+         * |       |          |
+         * |       |__________| -1
+         * |                  |
+         * 0       p1          1
+         */
+        synth_out = -1 + 2 * (phase < chan->p1);
+        break;
 
-            if( sd < synth->par[c][2]){ /* in rising Part of period */
-                r = -1.0 + 2.0 * sd / synth->par[c][2];
-            }else{    /* falling part */
-                r = 1.0 - 2.0 *
-                    (sd-synth->par[c][2])/(1-synth->par[c][2]);
-            }
-            break;
-        case SYNTH_TRAPETZ:
-            /* |    ______             |+1
-             * |   /      \            |
-             * |__/________\___________| 0
-             * | /          \          |
-             * |/            \_________|-1
-             * |                       |
-             * 0   p2    p3   p4       1
-             */
-            if( sd < synth->par[c][2]){ /* in rising part of period */
-                r = -1.0 + 2.0 * sd / synth->par[c][2];
-            }else if( sd < synth->par[c][3]){ /* in constant Part of period */
-                r=1.0;
-            }else if( sd < synth->par[c][4] ){ /* falling part */
-                r = 1.0 - 2.0 *
-                    (sd - synth->par[c][3])/(synth->par[c][4]-synth->par[c][3]);
-            }else{
-                r = -1.0;
-            }
-            break;
+      case synth_sawtooth:
+        /* |           __| +1
+         * |        __/  |
+         * |_______/_____|  0
+         * |  __/        |
+         * |_/           | -1
+         * |             |
+         * 0             1
+         */
+        synth_out = -1 + 2 * phase;
+        break;
 
-        case SYNTH_EXP:
-            /* |             |              | +1
-             * |            | |             |
-             * |          _|   |_           | 0
-             * |       __-       -__        |
-             * |____---             ---____ | f(p3) 
-             * |                            |
-             * 0             p2             1
-             */
-            move=exp( - synth->par[c][3] * LOG_10_20 * 100.0 ); /* 0 ..  1 */
-            if ( sd < synth->par[c][2] ) {
-                r = move * exp(sd * log(1.0/move)/synth->par[c][2]);
-            }else{
-                r = move * 
-                    exp( (1-sd)*log(1.0/move)/
-                         (1.0-synth->par[c][2]));
-            }
+      case synth_triangle:
+        /* |    .    | +1
+         * |   / \   |
+         * |__/___\__|  0
+         * | /     \ |
+         * |/       \| -1
+         * |         |
+         * 0   p1    1
+         */
 
-            /* r in 0 .. 1 */
-            r = r * 2.0 - 1.0; /* -1 .. +1 */
-            break;
-        case SYNTH_WHITENOISE:
-            r= 2.0* rand()/(double)RAND_MAX - 1.0;
-            break;
-        case SYNTH_PINKNOISE:
-            r = GeneratePinkNoise( &(synth->pinkn[c]) );
-            break;
-        case SYNTH_BROWNNOISE:
-            /* no idea if this algorithm is good enough.. */
-            move = 2.0* rand()/(double)RAND_MAX - 1.0;
-            move *= BROWNNOISE_FAC;
-            synth->h[c] += move;
-            if ((synth->h[c]) > 1.0)
-                synth->h[c] -= 2.0*move;
-            if ((synth->h[c]) < -1.0)
-                synth->h[c] += 2.0*move;
-            r=synth->h[c];
-            break;
-        default:
-            sox_warn("synth: internal error 1");
-            break;
-    }
+        if (phase < chan->p1)
+          synth_out = -1 + 2 * phase / chan->p1;          /* In rising part of period */
+        else
+          synth_out = 1 - 2 * (phase - chan->p1) / (1 - chan->p1); /* In falling part */
+        break;
 
-    /* add offset, but prevent clipping */
-    om = fabs(synth->par[c][0]);
-    if( om <= 1.0 ){
-        r *= 1.0 - om; /* reduce amp, prevent clipping */
-        r += om;
-    }
+      case synth_trapezium:
+        /* |    ______             |+1
+         * |   /      \            |
+         * |__/________\___________| 0
+         * | /          \          |
+         * |/            \_________|-1
+         * |                       |
+         * 0   p1    p2   p3       1
+         */
+        if (phase < chan->p1)       /* In rising part of period */
+          synth_out = -1 + 2 * phase / chan->p1;
+        else if (phase < chan->p2)  /* In high part of period */
+          synth_out = 1;
+        else if (phase < chan->p3)  /* In falling part */
+          synth_out = 1 - 2 * (phase - chan->p2) / (chan->p3 - chan->p2);
+        else                        /* In low part of period */
+          synth_out = -1;
+        break;
 
+      case synth_exp:
+        /* |             |              | +1
+         * |            | |             |
+         * |          _|   |_           | 0
+         * |       __-       -__        |
+         * |____---             ---____ | f(p2)
+         * |                            |
+         * 0             p1             1
+         */
+#define LOG_10_20        0.1151292546497022842009e0
+        synth_out = exp(-chan->p2 * LOG_10_20 * 100);  /* 0 ..  1 */
+        if (phase < chan->p1)
+          synth_out = synth_out * exp(phase * log(1 / synth_out) / chan->p1);
+        else
+          synth_out = synth_out * exp((1 - phase) * log(1 / synth_out) / (1 - chan->p1));
+        synth_out = synth_out * 2 - 1;      /* map 0 .. 1 to -1 .. +1 */
+        break;
 
-    switch(synth->mix[c]){
-        case SYNTH_CREATE:
-            ov = synth->max * r;
-            break;
-        case SYNTH_MIX:
-            ov = iv/2 + r*synth->max/2;
-            break;
-        case SYNTH_AMOD:
-            ov = (sox_ssample_t)(0.5*(r+1.0)*(double)iv);
-            break;
-        case SYNTH_FMOD:
-            ov = iv * r ;
-            break;
-        default:
-            sox_fail("synth: internal error 2");
-            break;
+      default: synth_out = 0;
     }
+  } else switch (chan->type) {
+#define RAND (2. * rand() * (1. / RAND_MAX) - 1)
+    case synth_whitenoise:
+      synth_out = RAND;
+      break;
 
-    return ov;
+    case synth_pinknoise:
+      synth_out = GeneratePinkNoise(&(chan->pink_noise));
+      break;
+
+    case synth_brownnoise:
+      do synth_out = chan->brown_noise + RAND * (1. / 16);
+      while (fabs(synth_out) > 1);
+      chan->brown_noise = synth_out;
+      break;
+
+    default: synth_out = 0;
+  }
+
+  /* Add offset, but prevent clipping: */
+  synth_out = synth_out * (1 - fabs(chan->offset)) + chan->offset;
+
+  switch (chan->combine) {
+    case synth_create: return  synth_out * synth->max;
+    case synth_mix   : return (synth_out * synth->max + synth_input) * 0.5;
+    case synth_amod  : return (synth_out + 1) * synth_input * 0.5;
+    case synth_fmod  : return  synth_out * synth_input;
+  }
+  return 0;
 }
 
 
 
-/*
- * Processed signed long samples from ibuf to obuf.
- */
-int sox_synth_flow(eff_t effp, const sox_ssample_t *ibuf, sox_ssample_t *obuf, 
-                  sox_size_t *isamp, sox_size_t *osamp)
+static int flow(eff_t effp, const sox_ssample_t * ibuf, sox_ssample_t * obuf,
+    sox_size_t * isamp, sox_size_t * osamp)
 {
-    synth_t synth = (synth_t) effp->priv;
-    int len; /* number of input samples */
-    int done = 0;
-    int c;
-    int chan=effp->ininfo.channels;
-    int result = SOX_SUCCESS;
+  synth_t synth = (synth_t) effp->priv;
+  unsigned len = min(*isamp, *osamp) / effp->ininfo.channels;
+  unsigned c, done, result = SOX_SUCCESS;
 
-    if(chan > MAXCHAN ){
-        sox_fail("synth: can not operate with more than %d channels",MAXCHAN);
-        return(SOX_EOF);
-    }
+  for (done = 0; done < len && result == SOX_SUCCESS; ++done) {
+    for (c = 0; c < effp->ininfo.channels; c++)
+      *obuf++ = do_synth(*ibuf++, synth, c, effp->ininfo.rate);
+    if (++synth->samples_done == synth->samples_to_do)
+      result = SOX_EOF;
+  }
+  *isamp = *osamp = done * effp->ininfo.channels;
+  return result;
+}
 
-    len = ((*isamp > *osamp) ? *osamp : *isamp) / chan;
 
-    while (done < len && result == SOX_SUCCESS)
-    {
-        for(c=0;c<chan;c++){
-            /* each channel is independent, but the algorithm is the same */
 
-            obuf[c] = do_synth(ibuf[c],synth,c);
-        }
-        ibuf+=chan;
-        obuf+=chan;
-        ++done;
-        synth->samples_done++;
-        if (synth->length > 0 && synth->samples_done == synth->length)
-        {
-            result = SOX_EOF;
-        }
-    }
-    *isamp = *osamp = done * chan;
-    return result;
+static int kill(eff_t effp)
+{
+  synth_t synth = (synth_t) effp->priv;
+  free(synth->channels);
+  free(synth->length_str);
+  return SOX_SUCCESS;
 }
 
-static sox_effect_t sox_synth_effect = {
-  "synth",
-  "Usage: synth [len] {[type] [combine] [freq[-freq2]] [off] [ph] [p1] [p2] [p3]}",
-  SOX_EFF_MCHAN,
-  sox_synth_getopts,
-  sox_synth_start,
-  sox_synth_flow,
-  sox_effect_nothing_drain,
-  sox_effect_nothing,
-  sox_effect_nothing
-};
 
+
 const sox_effect_t *sox_synth_effect_fn(void)
 {
-    return &sox_synth_effect;
+  static sox_effect_t driver = {
+    "synth",
+    "Usage: synth [len] {type [combine] [freq[-freq2] [off [ph [p1 [p2 [p3]]]]]]}",
+    SOX_EFF_MCHAN, getopts, start, flow, 0, 0, kill
+  };
+  return &driver;
 }
-/*-------------------------------------------------------------- end of file */
-