shithub: sox

Download patch

ref: 8a4f90fa0ac35ff2e742c904b2572ed4475e58ae
parent: 8c54fac1999ec9ebb429cf0e0f3c40de2b9127d6
author: cbagwell <cbagwell>
date: Sat Dec 4 17:13:16 EST 1999

Major library support updates.  Stereo GSM support.

--- a/TODO
+++ b/TODO
@@ -6,11 +6,9 @@
     you can grab the source code from the .au handler and software
     is avaliable from public sources for the rest.
 
-  o Add GSM support to WAV format.  Goes with the above but used
-    most often.
-
   o highpass and lowpass filters don't let you specify the cut
     of freq.  Need some improvements to these filters.
+    SJB: The old highp.c filter is IMHO broken, need to review that code.
 
   o Create a version of OSS and Sun driver that can play and record from the
     same device in duplex.
@@ -18,6 +16,8 @@
   o Internally sox can handle multiple effects on a given sound file.
     Add support for this from the command line.
 
+  o Add support for mixing multiple inputs from command line.
+
   o Endian checks are probably invalid on 64-bit machines.  Need to use
     something a more constant size the "int".  Probably should make it
     at least a util function/macro that can be overriden at compile time.
@@ -30,7 +30,7 @@
     the urge to come up with their own file format.
 
   o More effects!  I don't know DSP at all.  A Pitch Shifter is high
-    on the list.
+    on the list.  Spectrum analyser, etc, using FFTW lib.
 
   o Loop support for all formats that know about it.  WAV has
     some support for loops ("cue-points") but no support for
--- a/libst.c
+++ b/libst.c
@@ -35,7 +35,7 @@
 st_linear_to_ulaw( sample )
 short sample;
     {
-    static const int exp_lut[256] = 
+    static const unsigned char exp_lut[256] = 
 			      {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
                                4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
                                5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
@@ -107,7 +107,7 @@
 
 #else
 
-unsigned char ulaw_comp_table[16384] = {
+const unsigned char ulaw_comp_table[16384] = {
 	0xff,0xfe,0xfe,0xfd,0xfd,0xfc,0xfc,0xfb,
 	0xfb,0xfa,0xfa,0xf9,0xf9,0xf8,0xf8,0xf7,
 	0xf7,0xf6,0xf6,0xf5,0xf5,0xf4,0xf4,0xf3,
@@ -2157,7 +2157,7 @@
 	0x77,0x77,0x78,0x78,0x79,0x79,0x7a,0x7a,
 	0x7b,0x7b,0x7c,0x7c,0x7d,0x7d,0x7e,0x7e};
 
-int ulaw_exp_table[256] = {
+const short ulaw_exp_table[256] = {
 	 -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956,
 	 -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764,
 	 -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412,
@@ -2211,7 +2211,8 @@
 st_linear_to_Alaw( sample )
 short sample;
     {
-    static int exp_lut[128] = {1,1,2,2,3,3,3,3,
+    static const unsigned char exp_lut[128] =
+                              {1,1,2,2,3,3,3,3,
                                4,4,4,4,4,4,4,4,
                                5,5,5,5,5,5,5,5,
                                5,5,5,5,5,5,5,5,
@@ -2275,7 +2276,7 @@
 
 #else 
 
-unsigned char Alaw_comp_table[16384] = {
+unsigned const char Alaw_comp_table[16384] = {
 	 0xD5,0xD5,0xD5,0xD5,0xD4,0xD4,0xD4,0xD4,
 	 0xD7,0xD7,0xD7,0xD7,0xD6,0xD6,0xD6,0xD6,
 	 0xD1,0xD1,0xD1,0xD1,0xD0,0xD0,0xD0,0xD0,
@@ -4325,7 +4326,7 @@
 	 0x51,0x56,0x56,0x56,0x56,0x57,0x57,0x57,
 	 0x57,0x54,0x54,0x54,0x54,0x55,0x55,0x55};
 
-int Alaw_exp_table[256] = {
+const short Alaw_exp_table[256] = {
 	  -5504, -5248, -6016, -5760, -4480, -4224, -4992, -4736,
 	  -7552, -7296, -8064, -7808, -6528, -6272, -7040, -6784,
 	  -2752, -2624, -3008, -2880, -2240, -2112, -2496, -2368,
--- a/libst.h
+++ b/libst.h
@@ -20,43 +20,21 @@
 /******************************************************************/
 #ifdef FAST_ULAW_CONVERSION
 extern int           ulaw_exp_table[256];
-extern unsigned char ulaw_comp_table[16384];
+extern const unsigned char ulaw_comp_table[16384];
 #define st_ulaw_to_linear(ulawbyte)   ulaw_exp_table[ulawbyte]
 #define st_linear_to_ulaw(linearword) ulaw_comp_table[((short)linearword / 4) & 0x3fff]
 #else
-unsigned char st_linear_to_ulaw( /* short sample */ );
-int           st_ulaw_to_linear( /* unsigned char ulawbyte */ );
+unsigned char st_linear_to_ulaw(P1(short));
+int           st_ulaw_to_linear(P1(unsigned char));
 #endif
 
 #ifdef FAST_ALAW_CONVERSION
-extern int           Alaw_exp_table[256];
-extern unsigned char Alaw_comp_table[16384];
+extern const short         Alaw_exp_table[256];
+extern const unsigned char Alaw_comp_table[16384];
 #define st_Alaw_to_linear(Alawbyte)   Alaw_exp_table[Alawbyte]
 #define st_linear_to_Alaw(linearword) Alaw_comp_table[((short)linearword / 4) & 0x3fff]
 #else
-unsigned char st_linear_to_Alaw( /* short sample */ );
-int           st_Alaw_to_linear( /* unsigned char ulawbyte */ );
+unsigned char st_linear_to_Alaw(P1(short));
+int           st_Alaw_to_linear(P1(unsigned char));
 #endif
 
-/* Greatest Common Demoninator */
-LONG st_gcd(/* a, b */);
-/* Least Common Multiple */
-LONG st_lcm(/* a, b */);
-
-/****************************************************/
-/* Prototypes for internal cross-platform functions */
-/****************************************************/
-
-#ifndef HAVE_RAND
-extern int rand();
-extern void srand(/* seed */);
-#endif
-extern void st_initrand();
-
-extern LONG st_clip24();
-extern void st_sine();
-extern void st_triangle();
-
-#ifndef HAVE_STRERROR
-char *strerror(/*errorcode*/);
-#endif
--- a/pick.c
+++ b/pick.c
@@ -16,7 +16,6 @@
  */
 
 #include "st.h"
-#include "libst.h"
 
 /* Private data for SKEL file */
 typedef struct pickstuff {
--- a/src/8svx.c
+++ b/src/8svx.c
@@ -12,20 +12,16 @@
 #include <unistd.h>	/* For SEEK_* defines if not found in stdio */
 #endif
 
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-
 #include "st.h"
 
 /* Private data used by writer */
 struct svxpriv {
-        ULONG nsamples;
+	ULONG nsamples;
 	FILE *ch[4];
 };
 
-void svxwriteheader(P2(ft_t, LONG));
-    
+static void svxwriteheader(P2(ft_t, LONG));
+
 /*======================================================================*/
 /*                         8SVXSTARTREAD                                */
 /*======================================================================*/
@@ -307,7 +303,7 @@
 /*                         8SVXWRITEHEADER                              */
 /*======================================================================*/
 #define SVXHEADERSIZE 100
-void svxwriteheader(ft,nsamples)
+static void svxwriteheader(ft,nsamples)
 ft_t ft;
 LONG nsamples;
 {
--- a/src/adpcm.c
+++ b/src/adpcm.c
@@ -51,7 +51,8 @@
 /* these are step-size adjust factors, where
  * 1.0 is scaled to 0x100
  */
-static LONG stepAdjustTable[] = {
+static const
+LONG stepAdjustTable[] = {
 	230, 230, 230, 230, 307, 409, 512, 614,
 	768, 614, 512, 409, 307, 230, 230, 230
 };
@@ -60,7 +61,7 @@
    appear in the actual WAVE file.  They should be read in
    in case a sound program added extras to the list. */
 
-short iCoef[7][2] = {
+const short iCoef[7][2] = {
 			{ 256,   0},
 			{ 512,-256},
 			{   0,   0},
--- a/src/adpcm.h
+++ b/src/adpcm.h
@@ -8,12 +8,15 @@
 #define LONG long
 #endif
 /* FIXME: This breaks on Alphas! */
+/* SJB: Not really, because when used with sox, LONG and ULONG are
+ *      already defined before adpcm.h is included
+ */
 #ifndef ULONG
-#define ULONG u_long
+#define ULONG unsigned long
 #endif
 
 /* default coef sets */
-extern short iCoef[7][2];
+extern const short iCoef[7][2];
 
 /* AdpcmBlockExpandI() outputs interleaved samples into one output buffer */
 extern const char *AdpcmBlockExpandI(
--- a/src/aiff.c
+++ b/src/aiff.c
@@ -33,6 +33,8 @@
  *   for endianness was being performed AFTER reading the header instead
  *   of before reading it.
  *
+ * Nov 25, 1999 - internal functions made static
+ *
  */
 
 #include <math.h>
@@ -44,10 +46,6 @@
 #include <unistd.h>	/* For SEEK_* defines if not found in stdio */
 #endif
 
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-
 #include "st.h"
 
 /* Private data used by writer */
@@ -55,13 +53,14 @@
 	ULONG nsamples;	/* number of 1-channel samples read or written */
 };
 
-double read_ieee_extended();
-void aiffwriteheader(P2(ft_t, LONG));
-void write_ieee_extended(P2(ft_t, double));
-double ConvertFromIeeeExtended();
-void ConvertToIeeeExtended(P2(double, char *));
-void textChunk(P3(char **text, char *chunkDescription, ft_t ft));
-void reportInstrument(P1(ft_t ft));
+/* forward declarations */
+static double read_ieee_extended(P1(ft_t));
+static void aiffwriteheader(P2(ft_t, LONG));
+static void write_ieee_extended(P2(ft_t, double));
+static double ConvertFromIeeeExtended(P1(unsigned char*));
+static void ConvertToIeeeExtended(P2(double, char *));
+static void textChunk(P3(char **text, char *chunkDescription, ft_t ft));
+static void reportInstrument(P1(ft_t ft));
 
 void aiffstartread(ft) 
 ft_t ft;
@@ -366,7 +365,7 @@
 }
 
 /* print out the MIDI key allocations, loop points, directions etc */
-void reportInstrument(ft)
+static void reportInstrument(ft)
 ft_t ft;
 {
   int loopNum;
@@ -393,7 +392,7 @@
 }
 
 /* Process a text chunk, allocate memory, display it if verbose and return */
-void textChunk(text, chunkDescription, ft) 
+static void textChunk(text, chunkDescription, ft) 
 char **text;
 char *chunkDescription;
 ft_t ft;
@@ -532,7 +531,7 @@
 	aiffwriteheader(ft, p->nsamples / ft->info.channels);
 }
 
-void aiffwriteheader(ft, nframes)
+static void aiffwriteheader(ft, nframes)
 ft_t ft;
 LONG nframes;
 {
@@ -635,7 +634,7 @@
 	wlong(ft, (LONG) 0); /* block size */
 }
 
-double read_ieee_extended(ft)
+static double read_ieee_extended(ft)
 ft_t ft;
 {
 	char buf[10];
@@ -644,7 +643,7 @@
 	return ConvertFromIeeeExtended(buf);
 }
 
-void write_ieee_extended(ft, x)
+static void write_ieee_extended(ft, x)
 ft_t ft;
 double x;
 {
@@ -701,7 +700,7 @@
 
 # define FloatToUnsigned(f)      ((ULONG)(((LONG)(f - 2147483648.0)) + 2147483647L) + 1)
 
-void ConvertToIeeeExtended(num, bytes)
+static void ConvertToIeeeExtended(num, bytes)
 double num;
 char *bytes;
 {
@@ -800,7 +799,7 @@
  * Extended precision IEEE floating-point conversion routine.
  ****************************************************************/
 
-double ConvertFromIeeeExtended(bytes)
+static double ConvertFromIeeeExtended(bytes)
 unsigned char *bytes;	/* LCN */
 {
     double    f;
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -13,9 +13,6 @@
  * based on info grabed from aplay.c in alsa-utils package.
  */
 
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <unistd.h>
 #include <stdlib.h>
 #include <string.h>
@@ -24,22 +21,7 @@
 #include <linux/asound.h>
 #include <sys/ioctl.h>
 #include "st.h"
-#include "libst.h"
 
-char *get_style(style)
-int style;
-{
-    switch(style)
-    {
-	case UNSIGNED: return "UNSIGNED";
-	case SIGN2: return "SIGNED LINEAR 2's Comp";
-	case ULAW: return "uLaw";
-	case ALAW: return "aLaw";
-	case ADPCM: return "ADPCM";
-	default: return "unknown";
-    }
-}
-
 /*
  * Do anything required before you start reading samples.
  * Read file header.
@@ -94,7 +76,7 @@
 		fmt = SND_PCM_SFMT_U8;
 		break;
 	    default:
-		fail("Hardware does not support %s output", get_style(ft->info.style));
+		fail("Hardware does not support %s output", styles[ft->info.style]);
 		break;
 	}
     }
@@ -112,7 +94,7 @@
 		fmt = SND_PCM_SFMT_U16_LE;
 		break;
 	    default:
-		fail("Hardware does not support %s output", get_style(ft->info.style));
+		fail("Hardware does not support %s output", styles[ft->info.style]);
 		break;
 	}
     }
@@ -186,7 +168,7 @@
 		fmt = SND_PCM_SFMT_U8;
 		break;
 	    default:
-		fail("Hardware does not support %s output", get_style(ft->info.style));
+		fail("Hardware does not support %s output", styles[ft->info.style]);
 		break;
 	}
     }
@@ -204,7 +186,7 @@
 		fmt = SND_PCM_SFMT_U16_LE;
 		break;
 	    default:
-		fail("Hardware does not support %s output", get_style(ft->info.style));
+		fail("Hardware does not support %s output", styles[ft->info.style]);
 		break;
 	}
     }
--- a/src/au.c
+++ b/src/au.c
@@ -22,9 +22,6 @@
 #include "st.h"
 #include "g72x.h"
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 
 /* Magic numbers used in Sun and NeXT audio files */
 #define SUN_MAGIC 	0x2e736e64		/* Really '.snd' */
@@ -58,7 +55,7 @@
 	int in_bits;
 };
 
-void auwriteheader(P2(ft_t ft, ULONG data_size));
+static void auwriteheader(P2(ft_t ft, ULONG data_size));
 
 void austartread(ft) 
 ft_t ft;
@@ -243,7 +240,7 @@
  * Returns 1 if there is residual input, returns -1 if eof, else returns 0.
  * (Adapted from Sun's decode.c.)
  */
-int
+static int
 unpack_input(ft, code)
 ft_t			ft;
 unsigned char		*code;
@@ -311,7 +308,7 @@
 	auwriteheader(ft, p->data_size);
 }
 
-void auwriteheader(ft, data_size)
+static void auwriteheader(ft, data_size)
 ft_t ft;
 ULONG data_size;
 {
--- a/src/auto.c
+++ b/src/auto.c
@@ -17,8 +17,6 @@
 #include "st.h"
 #include <string.h>
 
-void gettype();
-
 void autostartread(ft)
 ft_t ft;
 {
--- a/src/chorus.c
+++ b/src/chorus.c
@@ -63,13 +63,9 @@
  */
 
 #include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <math.h>
 #include <string.h>
 #include "st.h"
-#include "libst.h"
 
 #define MOD_SINE	0
 #define MOD_TRIANGLE	1
@@ -175,10 +171,12 @@
 				sizeof(int) * chorus->length[i]);
 		if ( chorus->modulation[i] == MOD_SINE )
 			st_sine(chorus->lookup_tab[i], chorus->length[i], 
-				chorus->samples[i] - 1, chorus->depth_samples[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]);
+				chorus->samples[i] - 1,
+				chorus->depth_samples[i]);
 		chorus->phase[i] = 0;
 
 		if ( chorus->samples[i] > chorus->maxsamples )
--- a/src/cvsd.c
+++ b/src/cvsd.c
@@ -47,7 +47,6 @@
 
 #include "cvsdfilt.h"
 #include "st.h"
-#include "libst.h"
 
 /* ---------------------------------------------------------------------- */
 
@@ -97,7 +96,7 @@
 
 /* ---------------------------------------------------------------------- */
 
-float float_conv(fp1, fp2, n)
+static float float_conv(fp1, fp2, n)
 float *fp1;
 float *fp2;
 int n;
@@ -428,7 +427,7 @@
 #define DVMS_HEADER_LEN 120
 
 /* ---------------------------------------------------------------------- */
-
+/* SJB: should these be in misc.c instead? */
 static ULONG get32(p)
 unsigned char **p;
 {
--- a/src/dat.c
+++ b/src/dat.c
@@ -21,7 +21,6 @@
  */
 
 #include "st.h"
-#include "libst.h"
 
 /* Private data for dat file */
 typedef struct dat {
@@ -28,12 +27,13 @@
 	double timevalue, deltat;
 } *dat_t;
 
-LONG roundoff(x)
+/* SJB: should this be moved to misc.c ? */
+static LONG roundoff(x)
 double x;
 {
     if (x < 0.0) return(x - 0.5);
     else return(x + 0.5);
-    }
+}
 
 void
 datstartread(ft)
--- a/src/echo.c
+++ b/src/echo.c
@@ -56,12 +56,8 @@
  */
 
 #include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <math.h>
 #include "st.h"
-#include "libst.h"
 
 #define DELAY_BUFSIZ ( 50L * MAXRATE )
 #define MAX_ECHOS 7	/* 24 bit x ( 1 + MAX_ECHOS ) = */
--- a/src/echos.c
+++ b/src/echos.c
@@ -47,12 +47,8 @@
  */
 
 #include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <math.h>
 #include "st.h"
-#include "libst.h"
 
 #define DELAY_BUFSIZ ( 50L * MAXRATE )
 #define MAX_ECHOS 7	/* 24 bit x ( 1 + MAX_ECHOS ) = */
--- a/src/filter.c
+++ b/src/filter.c
@@ -39,9 +39,6 @@
 #include <math.h>
 #include <string.h>
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 
 #include "st.h"
 
@@ -69,6 +66,7 @@
 	Float *X, *Y;									/* I/O buffers */
 } *filter_t;
 
+/* makeFilter() declared in resample.c */
 extern int 
 makeFilter(P5(Float Fp[], LONG Nwing, double Froll, double Beta, LONG Num));
 
--- a/src/flanger.c
+++ b/src/flanger.c
@@ -53,9 +53,6 @@
  */
 
 #include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <math.h>
 #include <string.h>
 #include "st.h"
@@ -157,9 +154,11 @@
 
 	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;
--- a/src/gsm.c
+++ b/src/gsm.c
@@ -22,24 +22,39 @@
  * July 19, 1998 - Chris Bagwell (cbagwell@sprynet.com)
  *   Added GSM support to SOX from patches floating around with the help
  *   of Dima Barsky (ess2db@ee.surrey.ac.uk).
+ *
+ * Nov. 26, 1999 - Stan Brooks (stabro@megsinet.com)
+ *   Rewritten to support multiple channels
  */
 
 #include "st.h"
 #include "gsm.h"
 
+#define MAXCHANS 16
+
+/* sizeof(gsm_frame) */
+#define FRAMESIZE 33
+/* samples per gsm_frame */
+#define BLOCKSIZE 160
+
 /* Private data */
 struct gsmpriv {
-	gsm		handle;
-	gsm_signal	sample[160];
-	int		index;
+	int		channels;
+	gsm_signal	*samples;
+	gsm_signal	*samplePtr;
+	gsm_signal	*sampleTop;
+	gsm_byte *frames;
+	gsm		handle[MAXCHANS];
 };
 
-void
-gsmstartread(ft) 
+static void
+gsmstart_rw(ft,w) 
 ft_t ft;
+int w; /* w != 0 is write */
 {
 	struct gsmpriv *p = (struct gsmpriv *) ft->priv;
-
+	int ch;
+	
 	/* Sanity check */
 	if (sizeof(struct gsmpriv) > PRIVSIZE)
 		fail(
@@ -50,26 +65,32 @@
 	ft->info.size = BYTE;
 	if (!ft->info.rate)
 		ft->info.rate = 8000;
-	p->handle = gsm_create();
-	if (!p->handle)
-		fail("unable to create GSM stream");
-	p->index = 0;
+
+	p->channels = ft->info.channels;
+	if (p->channels > MAXCHANS || p->channels <= 0)
+		fail("gsm: channels(%d) must be in 1-16", ft->info.channels);
+
+	for (ch=0; ch<p->channels; ch++) {
+		p->handle[ch] = gsm_create();
+		if (!p->handle[ch])
+			fail("unable to create GSM stream");
+	}
+	p->frames = (gsm_byte*) malloc(p->channels*FRAMESIZE);
+	p->samples = (gsm_signal*) malloc(BLOCKSIZE * (p->channels+1) * sizeof(gsm_signal));
+	p->sampleTop = p->samples + BLOCKSIZE*p->channels;
+	p->samplePtr = (w)? p->samples : p->sampleTop;
 }
 
-void
-gsmstartwrite(ft)
+void gsmstartread(ft) 
 ft_t ft;
 {
-	struct gsmpriv *p = (struct gsmpriv *) ft->priv;
+	gsmstart_rw(ft,0);
+}
 
-	ft->info.style = GSM;
-	ft->info.size = BYTE;
-	if (!ft->info.rate)
-		ft->info.rate = 8000;
-	p->handle = gsm_create();
-	if (!p->handle)
-		fail("unable to create GSM stream");
-	p->index = 0;
+void gsmstartwrite(ft)
+ft_t ft;
+{
+	gsmstart_rw(ft,1);
 }
 
 /*
@@ -79,55 +100,94 @@
  * Return number of samples read.
  */
 
-LONG
-gsmread(ft, buf, samp)
+LONG gsmread(ft, buf, samp)
 ft_t ft;
 long *buf, samp;
 {
-	int bytes;
 	int done = 0;
-	gsm_frame	frame;
+	int r, ch, chans;
+	gsm_signal *gbuff;
 	struct gsmpriv *p = (struct gsmpriv *) ft->priv;
 
-	while (p->index && (p->index < 160) && (done < samp))
-		buf[done++] = LEFT(p->sample[p->index++], 16);
+	chans = p->channels;
 
 	while (done < samp)
 	{
-		p->index = 0;
-		bytes = fread( frame, 1, sizeof(frame), ft->fp );
-		if (bytes <= 0)
-			return done;
-		if (bytes < sizeof(frame))
-			fail("invalid frame size: %d bytes", bytes);
-		if (gsm_decode(p->handle, frame, p->sample) < 0)
-			fail("error during GSM decode");
-		while ((p->index < 160) && (done < samp))
-			buf[done++] = LEFT(p->sample[p->index++], 16);
+		while (p->samplePtr < p->sampleTop && done < samp)
+			buf[done++] = LEFT(*(p->samplePtr)++, 16);
+
+		if (done>=samp) break;
+
+		r = fread(p->frames, p->channels*FRAMESIZE, 1, ft->fp);
+		if (r != 1) break;
+
+		p->samplePtr = p->samples;
+		for (ch=0; ch<chans; ch++) {
+			int i;
+			gsm_signal *gsp;
+
+			gbuff = p->sampleTop;
+			if (gsm_decode(p->handle[ch], p->frames + ch*FRAMESIZE, gbuff) < 0)
+				fail("error during GSM decode");
+			
+			gsp = p->samples + ch;
+			for (i=0; i<BLOCKSIZE; i++) {
+				*gsp = *gbuff++;
+				gsp += chans;
+			}
+		}
 	}
 
 	return done;
 }
 
-int
-gsmwrite(ft, buf, samp)
+static void gsmflush(ft)
 ft_t ft;
+{
+	int r, ch, chans;
+	gsm_signal *gbuff;
+	struct gsmpriv *p = (struct gsmpriv *) ft->priv;
+
+	chans = p->channels;
+
+	/* zero-fill samples as needed */
+	while (p->samplePtr < p->sampleTop)
+		*(p->samplePtr)++ = 0;
+	
+	gbuff = p->sampleTop;
+	for (ch=0; ch<chans; ch++) {
+		int i;
+		gsm_signal *gsp;
+
+		gsp = p->samples + ch;
+		for (i=0; i<BLOCKSIZE; i++) {
+			gbuff[i] = *gsp;
+			gsp += chans;
+		}
+		gsm_encode(p->handle[ch], gbuff, p->frames);
+		r = fwrite(p->frames, FRAMESIZE, 1, ft->fp);
+		if (r != 1)
+			fail("write error");
+	}
+	p->samplePtr = p->samples;
+
+	return;
+}
+
+int gsmwrite(ft, buf, samp)
+ft_t ft;
 long *buf, samp;
 {
 	int done = 0;
-	gsm_frame	frame;
 	struct gsmpriv *p = (struct gsmpriv *) ft->priv;
 
 	while (done < samp)
 	{
-		while ((p->index < 160) && (done < samp))
-			p->sample[p->index++] = RIGHT(buf[done++], 16);
-		if (p->index < 160)
-			return done;
-		gsm_encode(p->handle, p->sample, frame);
-		if (fwrite(frame, 1, sizeof(frame), ft->fp) != sizeof(frame))
-			fail("write error");
-		p->index = 0;
+		while ((p->samplePtr < p->sampleTop) && (done < samp))
+			*(p->samplePtr)++ = RIGHT(buf[done++], 16);
+
+		if (p->samplePtr == p->sampleTop)
+			gsmflush(ft);
 	}
 
 	return done;
@@ -138,8 +198,13 @@
 ft_t ft;
 {
 	struct gsmpriv *p = (struct gsmpriv *) ft->priv;
+	int ch;
 
-	gsm_destroy(p->handle);
+	for (ch=0; ch<p->channels; ch++)
+		gsm_destroy(p->handle[ch]);
+
+	free(p->samples);
+	free(p->frames);
 }
 
 void
@@ -146,17 +211,11 @@
 gsmstopwrite(ft)
 ft_t ft;
 {
-	gsm_frame	frame;
 	struct gsmpriv *p = (struct gsmpriv *) ft->priv;
 
-	if (p->index)
-	{
-		while (p->index < 160)
-			p->sample[p->index++] = 0;
-		gsm_encode(p->handle, p->sample, frame);
-		if (fwrite(frame, 1, sizeof(frame), ft->fp) != sizeof(frame))
-			fail("write error");
-	}
-	gsm_destroy(p->handle);
+	if (p->samplePtr > p->samples)
+		gsmflush(ft);
+
+	gsmstopread(ft); /* destroy handles and free buffers */
 }
 #endif /* HAVE_LIBGSM */
--- a/src/hcom.c
+++ b/src/hcom.c
@@ -24,9 +24,6 @@
 #include "st.h"
 #include <string.h>
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 
 /* Dictionary entry for Huffman (de)compression */
 typedef struct {
@@ -50,7 +47,7 @@
 	short sample;
 };
 
-void skipbytes(P2(ft_t, int));
+static void skipbytes(P2(ft_t, int));
 
 void hcomstartread(ft)
 ft_t ft;
@@ -141,7 +138,7 @@
 	p->nrbits = -1; /* Special case to get first byte */
 }
 
-void skipbytes(ft, n)
+static void skipbytes(ft, n)
 ft_t ft;
 int n;
 {
@@ -301,6 +298,8 @@
    passed around in a structure instead. Use static so we don't
    polute global name space. */
 
+/* SJB: FIXME: dangerous static variables, need to analyse code */
+
 static dictent dictionary[511];
 static dictent *de;
 static LONG codes[256];
@@ -307,7 +306,7 @@
 static LONG codesize[256];
 static LONG checksum;
 
-void makecodes(e, c, s, b)
+static void makecodes(e, c, s, b)
 int e, c, s, b;
 {
   if(dictionary[e].dict_leftson < 0) {
@@ -322,7 +321,8 @@
 static LONG curword;
 static int nbits;
 
-void putlong(c, v)
+/* SJB: candidates for misc.c */
+static void putlong(c, v)
 unsigned char *c;
 LONG v;
 {
@@ -332,7 +332,7 @@
   *c++ = v & 0xff;
 }
 
-void putshort(c, v)
+static void putshort(c, v)
 unsigned char *c;
 short v;
 {
@@ -341,7 +341,7 @@
 }
 
 
-void putcode(c, df)
+static void putcode(c, df)
 unsigned char c;
 unsigned char ** df;
 {
@@ -364,7 +364,7 @@
   }
 }
 
-void compress(df, dl, fr)
+static void compress(df, dl, fr)
 unsigned char **df;
 LONG *dl;
 float fr;
@@ -472,7 +472,7 @@
   *dl = l;			/* and its compressed length */
 }
 
-void padbytes(ft, n)
+static void padbytes(ft, n)
 ft_t ft;
 int n;
 {
--- a/src/highp.c
+++ b/src/highp.c
@@ -19,6 +19,7 @@
  * 	A = 2.0 * pi * center
  * 	B = exp(-A / frequency)
  */
+/* SJB: Note: highp filter is currently broken, see test gnuplot graphs */
 
 #include <math.h>
 #include "st.h"
--- a/src/ima_rw.h
+++ b/src/ima_rw.h
@@ -22,6 +22,9 @@
 #	define SAMPL short
 #endif
 /* FIXME: This breaks on Alphas! */
+/* SJB: Not really, because when used with sox, LONG and ULONG are
+ *      already defined before adpcm.h is included
+ */
 #ifndef ULONG
 #	define ULONG unsigned long
 #endif
--- a/src/mask.c
+++ b/src/mask.c
@@ -15,7 +15,6 @@
 #include <stdlib.h>
 #include <math.h>
 #include "st.h"
-#include "libst.h"
 
 #define HALFABIT 1.44			/* square root of 2 */
 
--- a/src/maud.c
+++ b/src/maud.c
@@ -18,7 +18,6 @@
  */
 
 #include "st.h"
-#include "libst.h"
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -32,7 +31,7 @@
 	ULONG nsamples;
 };
 
-void maudwriteheader(P1(ft_t));
+static void maudwriteheader(P1(ft_t));
 
 /*
  * Do anything required before you start reading samples.
@@ -263,7 +262,7 @@
 }
 
 #define MAUDHEADERSIZE (4+(4+4+32)+(4+4+32)+(4+4))
-void maudwriteheader(ft)
+static void maudwriteheader(ft)
 ft_t ft;
 {
 	struct maudstuff * p = (struct maudstuff *) ft->priv;
--- a/src/misc.c
+++ b/src/misc.c
@@ -11,16 +11,15 @@
  * Sound Tools miscellaneous stuff.
  */
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <time.h>
-#include <math.h>
 #include "st.h"
-#include "libst.h"
 #include "version.h"
 #include "patchlvl.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
 
-char *sizes[] = {
+const char *sizes[] = {
 	"NONSENSE!",
 	"bytes",
 	"shorts",
@@ -31,7 +30,7 @@
 	"IEEE floats"
 };
 
-char *styles[] = {
+const char *styles[] = {
 	"NONSENSE!",
 	"unsigned",
 	"signed (2's complement)",
@@ -42,8 +41,8 @@
 	"gsm",
 };
 
-char readerr[] = "Premature EOF while reading sample file.";
-char writerr[] = "Error writing sample file.  You are probably out of disk space.";
+static const char readerr[] = "Premature EOF while reading sample file.";
+static const char writerr[] = "Error writing sample file.  You are probably out of disk space.";
 
 /* Utilities */
 
@@ -208,8 +207,8 @@
 
 
 /* dummy routines for do-nothing functions */
-void nothing() {}
-LONG nothing_success() {return(0);}
+void nothing(P0) {}
+LONG nothing_success(P0) {return(0);}
 
 /* dummy drain routine for effects */
 void null_drain(effp, obuf, osamp)
@@ -258,7 +257,7 @@
 #endif
 
 /* Util to set initial seed so that we are a little less non-random */
-void st_initrand() {
+void st_initrand(P0) {
 	time_t t;
 
 	time(&t);
@@ -278,14 +277,17 @@
 
 /* This was very painful.  We need a sine library. */
 
-void st_sine(buf, len, depth)
+void st_sine(buf, len, max, depth)
 int *buf;
-long len;
-long depth;
+LONG len;
+int max;
+int depth;
 {
 	long 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);
@@ -292,25 +294,28 @@
 	}
 }
 
-void st_triangle(buf, len, depth)
+void st_triangle(buf, len, max, depth)
 int *buf;
-long len;
-long depth;
+LONG len;
+int max;
+int depth;
 {
-	long i;
+	LONG i;
+	int offset;
 	double val;
 
+	offset = max - 2 * depth;
 	for (i = 0; i < len / 2; i++) {
 		val = i * 2.0 / len;
-		buf[i] = (int) (val * depth);
+		buf[i] = offset + (int) (val * 2.0 * (double)depth);
 	}
 	for (i = len / 2; i < len ; i++) {
 		val = (len - i) * 2.0 / len;
-		buf[i] = (int) (val * depth);
+		buf[i] = offset + (int) (val * 2.0 * (double)depth);
 	}
 }
 
-char *
+const char *
 version()
 {
 	static char versionstr[20];
--- a/src/oss.c
+++ b/src/oss.c
@@ -35,7 +35,6 @@
 #endif
 #include <sys/ioctl.h>
 #include "st.h"
-#include "libst.h"
 
 /*
  * Do anything required before you start reading samples.
--- a/src/phaser.c
+++ b/src/phaser.c
@@ -56,9 +56,6 @@
  */
 
 #include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <math.h>
 #include <string.h>
 #include "st.h"
@@ -154,9 +151,11 @@
 
 	if ( phaser->modulation == MOD_SINE )
 		st_sine(phaser->lookup_tab, phaser->length, 
+			phaser->maxsamples - 1,
 			phaser->maxsamples - 1);
 	else
 		st_triangle(phaser->lookup_tab, phaser->length, 
+			(phaser->maxsamples - 1) * 2,
 			phaser->maxsamples - 1);
 	phaser->counter = 0;
 	phaser->phase = 0;
--- a/src/polyphas.c
+++ b/src/polyphas.c
@@ -31,11 +31,7 @@
 #include <math.h>
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include "st.h"
-#include "libst.h"
 
 #define Float float/*double*/
 #define ISCALE 0x10000
@@ -345,7 +341,7 @@
 
    buffer must already be allocated.
 */
-void fir_design(Float *buffer, int length, Float cutoff)
+static void fir_design(Float *buffer, int length, Float cutoff)
 {
     int j;
     double sum;
--- a/src/rate.c
+++ b/src/rate.c
@@ -18,7 +18,6 @@
 
 #include <math.h>
 #include "st.h"
-#include "libst.h"
 
 /*
  * Linear Interpolation.
--- a/src/raw.c
+++ b/src/raw.c
@@ -20,12 +20,7 @@
  */
 
 #include "st.h"
-#include "libst.h"
 
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
-
 #include <string.h>
 #include <stdlib.h>
 
@@ -35,6 +30,8 @@
 
 #define MAXWSPEED 1
 
+static void rawdefaults(P1(ft_t ft));
+
 void rawstartread(ft) 
 ft_t ft;
 {
@@ -57,7 +54,7 @@
 /* Read raw file data, and convert it to */
 /* the sox internal signed long format. */
 
-unsigned char blockgetc(ft)
+static unsigned char blockgetc(ft)
 ft_t ft;
 {
 	char rval;
@@ -78,7 +75,7 @@
 	return (rval);
 }
 
-/* Util to swap every 2 chars up to 'n' imes. */
+/* Util to reverse the n chars starting at p. */
 static void swapn(p, n)
 char *p;
 int n;
@@ -282,7 +279,7 @@
 	ft->file.pos = 0;
 }
 
-void blockputc(ft,c)
+static void blockputc(ft,c)
 ft_t ft;
 int c;
 {
@@ -479,8 +476,6 @@
 * Set parameters to the fixed parameters known for this format,
 * and change format to raw format.
 */
-
-void rawdefaults();
 
 #define STARTREAD(NAME,SIZE,STYLE) \
 void NAME(ft) \
--- a/src/resample.c
+++ b/src/resample.c
@@ -37,12 +37,19 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  *
  */
+/*
+ * SJB: [11/25/99]
+ * TODO: another idea for improvement...
+ * note that upsampling usually doesn't require interpolation,
+ * therefore is faster and more accurate than downsampling.
+ * Downsampling by an integer factor is also simple, since
+ * it just involves decimation if the input is already 
+ * lowpass-filtered to the output Nyquist freqency.
+ * Get the idea? :)
+ */
 
 #include <math.h>
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include "st.h"
 
 /* resample includes */
@@ -82,12 +89,13 @@
    Float *X, *Y;      /* I/O buffers */
 } *resample_t;
 
-void LpFilter(P5(double c[],
+static void LpFilter(P5(double c[],
 		LONG N,
 		double frq,
 		double Beta,
 		LONG Num));
 
+/* makeFilter is used by filter.c */
 int makeFilter(P5(Float Imp[],
 		  LONG Nwing,
 		  double Froll,
@@ -633,7 +641,7 @@
 
 #define IzeroEPSILON 1E-21               /* Max error acceptable in Izero */
 
-double Izero(x)
+static double Izero(x)
 double x;
 {
    double sum, u, halfx, temp;
@@ -651,7 +659,7 @@
    return(sum);
 }
 
-void LpFilter(c,N,frq,Beta,Num)
+static void LpFilter(c,N,frq,Beta,Num)
 double c[], frq, Beta;
 LONG N, Num;
 {
--- a/src/reverb.c
+++ b/src/reverb.c
@@ -93,12 +93,8 @@
  */
 
 #include <stdlib.h> /* Harmless, and prototypes atof() etc. --dgc */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <math.h>
 #include "st.h"
-#include "libst.h"
 
 #define REVERB_FADE_THRESH 10
 #define DELAY_BUFSIZ ( 50L * MAXRATE )
--- a/src/sf.c
+++ b/src/sf.c
@@ -25,9 +25,6 @@
 #endif
 #include <string.h>
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 
 /* Private data for SF file */
 typedef struct sfstuff {
@@ -38,7 +35,7 @@
  * Read the codes from the sound file, allocate space for the comment and
  * assign its pointer to the comment field in ft.
  */
-void readcodes(ft, sfhead)
+static void readcodes(ft, sfhead)
 ft_t ft;
 SFHEADER *sfhead;
 {
--- a/src/smp.c
+++ b/src/smp.c
@@ -103,7 +103,7 @@
 /*
  * set the trailer data - loops and markers, to reasonably benign values
  */
-void settrailer(ft, trailer, rate)
+static void settrailer(ft, trailer, rate)
 ft_t ft;
 struct smptrailer *trailer;
 unsigned int rate;
--- a/src/sndrtool.c
+++ b/src/sndrtool.c
@@ -26,8 +26,7 @@
 #define	SEEK_CUR	1
 #endif
 
-void  sndtwriteheader(P2(ft_t ft,LONG nsamples));
-void rawwrite(P3(ft_t, LONG *, LONG));
+static void  sndtwriteheader(P2(ft_t ft,LONG nsamples));
 
 /*======================================================================*/
 /*                         SNDSTARTREAD                                */
@@ -192,7 +191,7 @@
 /*======================================================================*/
 /*                         SNDTWRITEHEADER                              */
 /*======================================================================*/
-void sndtwriteheader(ft,nsamples)
+static void sndtwriteheader(ft,nsamples)
 ft_t ft;
 LONG nsamples;
 {
@@ -213,5 +212,4 @@
 sprintf (name_buf,"%s - File created by Sound Exchange",ft->filename);
 fwrite (name_buf, 1, 96, ft->fp);
 }
-
 
--- a/src/sox.c
+++ b/src/sox.c
@@ -12,6 +12,12 @@
  *
  * Change History:
  *
+ * Nov. 1, 1999 - Stan Brooks (stabro@megsinet.com)
+ *   Added -X input option, to extract range of samples.
+ *   Moved volume and dovolume from static variables into ft->info...
+ *   TODO: This should allow a way (kluge) of specifying multiple
+ *   inputs to be mixed together.
+ *
  * June 1, 1998 - Chris Bagwell (cbagwell@sprynet.com)
  *   Added patch to get volume working again.  Based on patch sent from
  *   Matija Nalis <mnalis@public.srce.hr>.
@@ -32,9 +38,6 @@
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>		/* for malloc() */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <errno.h>
 #include <sys/types.h>		/* for fstat() */
 #include <sys/stat.h>		/* for fstat() */
@@ -64,35 +67,39 @@
  * Rewrite for multiple effects: Aug 24, 1994.
  */
 
-int clipped = 0;		/* Volume change clipping errors */
+static int clipped = 0;		/* Volume change clipping errors */
+static int writing = 0;	/* are we writing to a file? */
+static int soxpreview = 0;	/* preview mode */
 
+
 static LONG ibufl[BUFSIZ/2];	/* Left/right interleave buffers */
 static LONG ibufr[BUFSIZ/2];	
 static LONG obufl[BUFSIZ/2];
 static LONG obufr[BUFSIZ/2];
 
-void init();
-void doopts(P2(int, char **));
-void usage(P1(char *));
-int filetype(P1(int));
-void process();
-void statistics();
-LONG volumechange();
-void checkeffect(P1(eff_t));
-int flow_effect(P1(int));
-int drain_effect(P1(int));
+/* local forward declarations */
+static void init(P0);
+static void doopts(P2(int, char **));
+static void usage(P1(char *))NORET;
+static int filetype(P1(int));
+static void process(P0);
+static void statistics(P0);
+static LONG volumechange(P3(LONG *buf, LONG ct, double vol));
+static void checkeffect(P1(eff_t));
+static int flow_effect(P1(int));
+static int drain_effect(P1(int));
 
-struct soundstream informat, outformat;
+static struct soundstream informat, outformat;
 
-ft_t ft;
+static ft_t ft;
 
 #define MAXEFF 4
-struct effect eff;
-struct effect efftab[MAXEFF];	/* table of left/mono channel effects */
-struct effect efftabR[MAXEFF];	/* table of right channel effects */
+static struct effect eff;
+static struct effect efftab[MAXEFF];	/* table of left/mono channel effects */
+static struct effect efftabR[MAXEFF];	/* table of right channel effects */
 				/* efftab[0] is the input stream */
-int neffects;			/* # of effects */
-char *ifile, *ofile, *itype, *otype;
+static int neffects;			/* # of effects */
+static char *ifile, *ofile;
 
 int main(argc, argv)
 int argc;
@@ -105,6 +112,7 @@
 
 	/* Get input format options */
 	ft = &informat;
+	clipped = 0;
 	doopts(argc, argv);
 	/* Get input file */
 	if (optind >= argc)
@@ -172,8 +180,8 @@
 	}
 
 	/* Check global arguments */
-	if (volume <= 0.0)
-		fail("Volume must be greater than 0.0");
+	if (informat.info.dovol && informat.info.vol == 0.0)
+		fail("Volume must be non-zero"); /* negative volume is phase-reversal */
 	
 	/* If file types have not been set with -t, set from file names. */
 	if (! informat.filetype) {
@@ -206,12 +214,12 @@
 }
 
 #ifdef HAVE_GETOPT_H
-char *getoptstr = "+r:v:t:c:phsuUAaigbwlfdDxV";
+static char *getoptstr = "+r:v:t:c:B:X:phsuUAaigbwlfdDxV";
 #else
-char *getoptstr = "r:v:t:c:phsuUAaigbwlfdDxV";
+static char *getoptstr = "r:v:t:c:B:X:phsuUAaigbwlfdDxV";
 #endif
 
-void doopts(argc, argv)
+static void doopts(argc, argv)
 int argc;
 char **argv;
 {
@@ -234,13 +242,16 @@
 			if (ft->filetype[0] == '.')
 				ft->filetype++;
 			break;
-#if 0
+
 		case 'X':  /* extract a subinterval of samples as input */
 			if (! ft) usage("-X");
-			ft->fileextract++;
-			ft->filetype = optarg;
+			str = optarg;
+			/* FIXME: allow "A-B" "-B" "A-" "A,B" also maybe lists of ranges */
+			if ( 2 != sscanf(str, "%lu-%lu", &ft->info.x0, &ft->info.x1))
+				fail("eXtract range '%s' is not valid", optarg);
+			if (ft->info.x1==0) ft->info.x1 -= 1; /* MAXULONG */
 			break;
-#endif
+
 		case 'r':
 			if (! ft) usage("-r");
 			str = optarg;
@@ -254,13 +265,11 @@
 				fail("-r must be given a positive integer");
 			break;
 		case 'v':
-			if (! ft) usage("-v");
+			if (!ft || ft->info.dovol) usage("-v");
 			str = optarg;
-			if ((! sscanf(str, "%e", &volume)) ||
-					(volume <= 0))
-				fail("Volume value '%s' is not a number",
-					optarg);
-			dovolume = 1;
+			if (! sscanf(str, "%lf", &ft->info.vol)) /* neg volume is ok */
+				fail("Volume value '%s' is not a number", optarg);
+			ft->info.dovol = 1;
 			break;
 
 		case 'c':
@@ -269,6 +278,12 @@
 			if (! sscanf(str, "%d", &ft->info.channels))
 				fail("-c must be given a number");
 			break;
+		case 'B':
+			if (! ft) usage("-B");
+			str = optarg;
+			if (! sscanf(str, "%hu", &ft->info.bs) || ft->info.bs<=0) /* blocksize */
+				fail("-B must be given a positive number");
+			break;
 		case 'b':
 			if (! ft) usage("-b");
 			ft->info.size = BYTE;
@@ -335,7 +350,7 @@
 	}
 }
 
-void init() {
+static void init(P0) {
 
 	/* init files */
 	informat.info.rate      = outformat.info.rate  = 0;
@@ -342,6 +357,12 @@
 	informat.info.size      = outformat.info.size  = -1;
 	informat.info.style     = outformat.info.style = -1;
 	informat.info.channels  = outformat.info.channels = -1;
+	informat.info.bs        = outformat.info.bs    = 0;
+	informat.info.dovol     = outformat.info.dovol = 0;
+	informat.info.vol       = outformat.info.vol   = 1.0;
+	informat.info.x         = outformat.info.x  = 0;
+	informat.info.x0        = outformat.info.x0 = 0;
+	informat.info.x1        = outformat.info.x1 = -1;
 	informat.comment   = outformat.comment = NULL;
 	informat.swap      = 0;
 	informat.filetype  = outformat.filetype  = (char *) 0;
@@ -356,7 +377,7 @@
  *	one buffer at a time
  */
 
-void process() {
+static void process(P0) {
     LONG i;
     int e, f, havedata;
 
@@ -368,8 +389,10 @@
     (* informat.h->startread)(&informat);
     checkformat(&informat);
     
-    if (dovolume)
-	report("Volume factor: %f\n", volume);
+    if (informat.info.dovol)
+	report("Volume factor: %f\n", informat.info.vol);
+    if (informat.info.x0 || informat.info.x1 != MAXULONG)
+	report("Extract samples %lu <= x < %lu\n", informat.info.x0, informat.info.x0);
     
     report("Input file: using sample rate %lu\n\tsize %s, style %s, %d %s",
 	   informat.info.rate, sizes[informat.info.size], 
@@ -461,19 +484,26 @@
 	    efftabR[e].obuf = (LONG *) malloc(BUFSIZ * sizeof(LONG));
     }
 
-    /* Read initial chunk of input data. */
-    efftab[0].olen = (*informat.h->read)(&informat, 
-					 efftab[0].obuf, (LONG) BUFSIZ);
-    efftab[0].odone = 0;
 
-    /* Change the volume of this initial input data if needed. */
-    if (dovolume)
-	for (i = 0; i < efftab[0].olen; i++)
-	    efftab[0].obuf[i] = volumechange(efftab[0].obuf[i]);
-
     /* Run input data through effects and get more until olen == 0 */
-    while (efftab[0].olen > 0) {
+    while (informat.info.x < informat.info.x1) {
+	ULONG ct, r;
 
+	/* Read (extracted) chunk of input data. */
+	ct = (informat.info.x < informat.info.x0)?
+	  (informat.info.x0 - informat.info.x) : (informat.info.x1 - informat.info.x);
+	if (ct > BUFSIZ) ct = BUFSIZ;
+	r = (*informat.h->read)(&informat, efftab[0].obuf, ct);
+	if (!r) break;
+	informat.info.x += r;
+	if (informat.info.x <= informat.info.x0) continue;
+	efftab[0].olen = r;
+	efftab[0].odone = 0;
+
+	/* Change the volume of this initial input data if needed. */
+	if (informat.info.dovol)
+	    clipped += volumechange(efftab[0].obuf, efftab[0].olen, informat.info.vol);
+
 	/* mark chain as empty */
 	for(e = 1; e < neffects; e++)
 	    efftab[e].odone = efftab[e].olen = 0;
@@ -502,15 +532,6 @@
 		}
 	} while (havedata);
 
-	/* Read another chunk of input data. */
-	efftab[0].olen = (*informat.h->read)(&informat, 
-		efftab[0].obuf, (LONG) BUFSIZ);
-	efftab[0].odone = 0;
-
-	/* Change volume of these samples if needed. */
-	if (dovolume)
-	    for (i = 0; i < efftab[0].olen; i++)
-		efftab[0].obuf[i] = volumechange(efftab[0].obuf[i]);
     }
 
     /* Drain the effects out first to last, 
@@ -553,7 +574,7 @@
         fclose(outformat.fp);
 }
 
-int flow_effect(e)
+static int flow_effect(e)
 int e;
 {
     LONG i, done, idone, odone, idonel, odonel, idoner, odoner;
@@ -617,7 +638,7 @@
     return 1;
 }
 
-int drain_effect(e)
+static int drain_effect(e)
 int e;
 {
     LONG i, olen, olenl, olenr;
@@ -671,7 +692,7 @@
  * Smart ruleset for multiple effects in sequence.
  * 	Puts user-specified effect in right place.
  */
-void
+static void
 checkeffect(effp)
 eff_t effp;
 {
@@ -849,30 +870,38 @@
 }
 
 /* Guido Van Rossum fix */
-void statistics() {
-	if (dovolume && clipped > 0)
+static void statistics(P0) {
+	if (informat.info.dovol && clipped > 0)
 		report("Volume change clipped %d samples", clipped);
 }
 
-LONG volumechange(y)
-LONG y;
+static LONG volumechange(buf, ct, vol)
+LONG *buf;
+LONG ct;
+double vol;
 {
-	double y1;
+	double y;
+	LONG *p,*top;
+	LONG clips=0;
 
-	y1 = y * volume;
-	if (y1 < -2147483647.0) {
-		y1 = -2147483647.0;
-		clipped++;
+	p = buf;
+	top = buf+ct;
+	while (p < top) {
+	    y = vol * *p;
+	    if (y < -2147483647.0) {
+		y = -2147483647.0;
+		clips++;
+	    }
+	    else if (y > 2147483647.0) {
+		y = 2147483647.0;
+		clips++;
+	    }
+	    *p++ = y + 0.5;
 	}
-	else if (y1 > 2147483647.0) {
-		y1 = 2147483647.0;
-		clipped++;
-	}
-
-	return y1;
+	return clips;
 }
 
-int filetype(fd)
+static int filetype(fd)
 int fd;
 {
 	struct stat st;
@@ -882,10 +911,10 @@
 	return st.st_mode & S_IFMT;
 }
 
-char *usagestr = 
+static char *usagestr = 
 "[ gopts ] [ fopts ] ifile [ fopts ] ofile [ effect [ effopts ] ]";
 
-void usage(opt)
+static void usage(opt)
 char *opt;
 {
     int i;
@@ -916,7 +945,7 @@
 
 
 /* called from util.c:fail */
-void cleanup() {
+void cleanup(P0) {
 	/* Close the input file and outputfile before exiting*/
 	if (informat.fp)
 		fclose(informat.fp);
--- a/src/st.h
+++ b/src/st.h
@@ -12,12 +12,20 @@
  */
 
 #include <stdio.h>
-
+#include <stdlib.h>
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
 #ifdef HAVE_BYTESWAP_H
 #include <byteswap.h>
 #endif
 
-/* FIXME: Move to seperate header */
+
+/* SJB: these may be changed to assist fail-recovery in libST */
+#define st_malloc malloc
+#define st_free free
+
+/* FIXME: Move to separate header */
 #ifdef __alpha__
 #include <sys/types.h>   /* To get defines for 32-bit integers */
 #define	LONG	int32_t
@@ -27,11 +35,17 @@
 #define ULONG	unsigned long
 #endif
 
+#define MAXLONG 0x7fffffffL
+#define MAXULONG 0xffffffff
+
+/* various gcc optimizations */
 #ifdef __GNUC__
 #define NORET __attribute__((noreturn))
+#define REGPARM(n) __attribute__((regparm(n)))
 #define INLINE inline
 #else
 #define NORET
+#define REGPARM(n)
 #define INLINE
 #endif
 
@@ -61,6 +75,12 @@
 	int		size;		/* word length of data */
 	int		style;		/* format of sample numbers */
 	int		channels;	/* number of sound channels */
+	unsigned short	bs;		/* requested blocksize, eg for output .wav's */
+	unsigned char	dovol;		/* has volume factor */
+	double		vol;		/* volume factor */
+	ULONG		x;		/* current sample number */
+	ULONG		x0;		/* 1st sample (if source) */
+	ULONG		x1;		/* top sample (if source) */
 };
 
 /* Loop parameters */
@@ -134,10 +154,6 @@
 	double	priv[PRIVSIZE/8];	/* format's private data area */
 };
 
-/* This shoul not be here.  Only needed in sox.c */
-extern struct soundstream informat;
-extern struct soundstream outformat;
-
 typedef struct soundstream *ft_t;
 
 /* FIXME: Prefix all #defines with ST_ */
@@ -146,7 +162,7 @@
 #define FILE_LOOPS	2	/* does file format support loops? */
 #define FILE_INSTR	4	/* does file format support instrument specificications? */
 
-/* Size field */
+/* Size field */ /* SJB: note that the 1st 3 are sometimes used as sizeof(type) */
 #define	BYTE	1
 #define	WORD	2
 #define	DWORD	4
@@ -166,8 +182,9 @@
 /* FIXME: This shouldn't be defined inside library.  Only needed
  * by sox.c itself.  Delete from raw.c and misc.c.
  */
-extern char *sizes[];
-extern char *styles[];
+/* declared in misc.c */
+extern const char *sizes[];
+extern const char *styles[];
 
 /*
  * Handler structure for each effect.
@@ -183,7 +200,7 @@
 	void	(*stop)();		/* finish up effect */
 } effect_t;
 
-extern effect_t effects[];
+extern effect_t effects[]; /* declared in handlers.c */
 
 #define	EFF_CHAN	1		/* Effect can mix channels up/down */
 #define EFF_RATE	2		/* Effect can alter data rate */
@@ -206,6 +223,7 @@
 
 /* FIXME: Move to internal st header */
 #if	defined(__STDC__)
+#define	P0 void
 #define	P1(a) a
 #define	P2(a,b) a, b
 #define	P3(a,b,c) a, b, c
@@ -217,6 +235,7 @@
 #define	P9(a,b,c,d,e,f,g,h,i) a, b, c, d, e, f, g, h, i
 #define	P10(a,b,c,d,e,f,g,h,i,j) a, b, c, d, e, f, g, h, i, j
 #else
+#define	P0
 #define	P1(a)
 #define	P2(a,b)
 #define	P3(a,b,c)
@@ -229,9 +248,31 @@
 #define	P10(a,b,c,d,e,f,g,h,i,j)
 #endif
 
+/* declared in misc.c */
+extern LONG st_clip24(P1(LONG)) REGPARM(1);
+extern void st_sine(P4(int *, LONG, int, int));
+extern void st_triangle(P4(int *, LONG, int, int));
+
+extern LONG st_gcd(P2(LONG,LONG)) REGPARM(2);
+extern LONG st_lcm(P2(LONG,LONG)) REGPARM(2);
+/****************************************************/
+/* Prototypes for internal cross-platform functions */
+/****************************************************/
+/* SJB: shouldn't these be elsewhere, exported from misc.c */
+#ifndef HAVE_RAND
+extern int rand(P0);
+extern void srand(P1(ULONG seed));
+#endif
+extern void st_initrand(P0);
+
+#ifndef HAVE_STRERROR
+char *strerror(P1(int errorcode));
+#endif
+
 /* Read and write basic data types from "ft" stream.  Uses ft->swap for
  * possible byte swapping.
  */
+/* declared in misc.c */
 unsigned short rshort(P1(ft_t ft));			
 unsigned short wshort(P2(ft_t ft, unsigned short us));
 ULONG          rlong(P1(ft_t ft));		
@@ -245,6 +286,7 @@
  * here for convience.  This wont last for long so application software
  * shouldn't make use of it.
  */
+/* declared in raw.c */
 void rawstartread(P1(ft_t ft));
 void rawstartwrite(P1(ft_t ft));
 void rawstopread(P1(ft_t ft));
@@ -265,9 +307,9 @@
 double 	       swapd(P1(double d));			/* Swap double */
 
 /* util.c */
-void report(P2(char *, ...));
-void warn(P2(char *, ...));
-void fail(P2(char *, ...))NORET;
+void report(P2(const char *, ...));
+void warn(P2(const char *, ...));
+void fail(P2(const char *, ...))NORET;
 
 void geteffect(P1(eff_t));
 void gettype(P1(ft_t));
@@ -281,23 +323,11 @@
  */
 void sigintreg(P1(ft_t));
 
-/* FIXME: Move to sox header file. */
-typedef	unsigned int u_i;
-typedef	ULONG u_l;
-typedef	unsigned short u_s;
-
-extern float volume;	/* expansion coefficient */
-extern int dovolume;
-
-extern int writing;	/* are we writing to a file? */
-
 /* export flags */
+/* FIXME: these declared in util.c, inappropriate for lib */
 extern int verbose;	/* be noisy on stderr */
-
 extern char *myname;
 
-extern int soxpreview;	/* Preview mode: be fast and ugly */
-
 /* FIXME: Not externally visible currently.  Its a per-effect value. */
 #define	MAXRATE	50L * 1024			/* maximum sample rate */
 
@@ -314,6 +344,6 @@
 
 #define REMOVE unlink
 
-char *version();			/* return version number */
+const char *version(P0);			/* return version number */
 
 #endif
--- a/src/stat.c
+++ b/src/stat.c
@@ -18,8 +18,6 @@
 #include <math.h>
 #include "st.h"
 
-#define MAXLONG 0x7fffffffL
-
 /* Private data for STAT effect */
 typedef struct statstuff {
 	double	min, max;
--- a/src/sunaudio.c
+++ b/src/sunaudio.c
@@ -26,12 +26,8 @@
 #include <malloc.h>
 #include <unistd.h>
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <fcntl.h>
 #include "st.h"
-#include "libst.h"
 
 /*
  * Do anything required before you start reading samples.
--- a/src/tx16w.c
+++ b/src/tx16w.c
@@ -60,9 +60,10 @@
     unused[2];       /* set these to null, to be on the safe side */
 } ;
 
-static unsigned char magic1[4] = {0, 0x06, 0x10, 0xF6};
-static unsigned char magic2[4] = {0, 0x52, 0x00, 0x52};
+static const unsigned char magic1[4] = {0, 0x06, 0x10, 0xF6};
+static const unsigned char magic2[4] = {0, 0x52, 0x00, 0x52};
 
+/* SJB: dangerous static variables */
 static LONG tx16w_len=0;
 static LONG writedone=0;
 
--- a/src/util.c
+++ b/src/util.c
@@ -28,22 +28,15 @@
  * and utility routines for other main programs to use.
  */
 
-
-float volume = 1.0;	/* expansion coefficient */
-int dovolume = 0;
-
-int writing = 0;	/* are we writing to a file? */
-
 /* export flags */
 int verbose = 0;	/* be noisy on stderr */
 
 char *myname = 0;
 
-int soxpreview = 0;	/* preview mode */
 
 
 void
-report(char *fmt, ...) 
+report(const char *fmt, ...) 
 {
 	va_list args;
 
@@ -59,7 +52,7 @@
 
 
 void
-warn(char *fmt, ...) 
+warn(const char *fmt, ...) 
 {
 	va_list args;
 
@@ -72,7 +65,7 @@
 }
 
 void
-fail(char *fmt, ...) 
+fail(const char *fmt, ...) 
 {
 	va_list args;
 	extern void cleanup();
--- a/src/vibro.c
+++ b/src/vibro.c
@@ -26,9 +26,6 @@
 
 #include <math.h>
 #include <stdlib.h>
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include "st.h"
 
 /* Private data for Vibro effect */
@@ -62,7 +59,7 @@
 
 /* This was very painful.  We need a sine library. */
 
-void sine(buf, len, depth)
+static void sine(buf, len, depth)
 short *buf;
 int len;
 float depth;
--- a/src/voc.c
+++ b/src/voc.c
@@ -167,9 +167,9 @@
 
 #define	min(a, b)	(((a) < (b)) ? (a) : (b))
 
-void getblock();
-void blockstart(P1(ft_t));
-void blockstop(P1(ft_t));
+static void getblock(P1(ft_t));
+static void blockstart(P1(ft_t));
+static void blockstop(P1(ft_t));
 
 void vocstartread(ft) 
 ft_t ft;
@@ -346,7 +346,7 @@
 /* Voc-file handlers */
 
 /* Read next block header, save info, leave position at start of data */
-void
+static void
 getblock(ft)
 ft_t ft;
 {
@@ -503,7 +503,7 @@
 }
 
 /* Start an output block. */
-void blockstart(ft)
+static void blockstart(ft)
 ft_t ft;
 {
 	vs_t v = (vs_t) ft->priv;
@@ -559,7 +559,7 @@
 }
 
 /* End the current data or silence block. */
-void blockstop(ft) 
+static void blockstop(ft) 
 ft_t ft;
 {
 	vs_t v = (vs_t) ft->priv;
--- a/src/wav.c
+++ b/src/wav.c
@@ -61,9 +61,6 @@
 
 #include <string.h>		/* Included for strncmp */
 #include <stdlib.h>		/* Included for malloc and free */
-#ifdef HAVE_MALLOC_H
-#include <malloc.h>
-#endif
 #include <stdio.h>
 
 #ifdef HAVE_UNISTD_H
@@ -83,7 +80,8 @@
 
 /* Private data for .wav file */
 typedef struct wavstuff {
-    LONG	   numSamples;
+    LONG	   numSamples;     /* reading: starts at total count and decremented  */
+    		                   /* writing: starts at 0 and counts samples written */
     LONG	   dataLength;     /* needed for ADPCM writing */
     unsigned short formatTag;	   /* What type of encoding file is using */
     unsigned short samplesPerBlock;
@@ -101,10 +99,10 @@
 
     /* following used by GSM 6.10 wav */
 #ifdef HAVE_LIBGSM
-    gsm  gsmhandle;
-    gsm_signal *gsmsample;
-    int  gsmindex;
-    int gsmbytecount;
+    gsm		   gsmhandle;
+    gsm_signal	   *gsmsample;
+    int		   gsmindex;
+    int		   gsmbytecount;    /* counts bytes written to data block */
 #endif
 } *wav_t;
 
@@ -116,9 +114,7 @@
 
 static char *wav_format_str();
 
-LONG rawread(P3(ft_t, LONG *, LONG));
-void rawwrite(P3(ft_t, LONG *, LONG));
-void wavwritehdr(P2(ft_t, int));
+static void wavwritehdr(P2(ft_t, int));
 
 
 /****************************************************************************/
@@ -282,7 +278,7 @@
     wav_t	wav = (wav_t) ft->priv;
     int done=0;
     int bytes;
-    gsm_frame	frame;
+    gsm_byte	frame[65];
 
   /* copy out any samples left from the last call */
     while(wav->gsmindex && (wav->gsmindex<160*2) && (done < len))
@@ -291,25 +287,20 @@
   /* read and decode loop, possibly leaving some samples in wav->gsmsample */
     while (done < len) {
 	wav->gsmindex=0;
-	/*read the long 33 byte half */
-	bytes = fread(frame,1,sizeof(frame),ft->fp);   
+	bytes = fread(frame,1,65,ft->fp);   
 	if (bytes <=0)
 	    return done;
-	if (bytes<sizeof(frame))
-	    fail("invalid wav gsm frame size: %d bytes",bytes);
+	if (bytes<65) {
+	    warn("invalid wav gsm frame size: %d bytes",bytes);
+	    return done;
+	}
+	/* decode the long 33 byte half */
 	if(gsm_decode(wav->gsmhandle,frame, wav->gsmsample)<0)
 	    fail("error during gsm decode");
-
-	/*read the short 32 byte half */
-	bytes = fread(frame,1,sizeof(frame)-1,ft->fp);   
-	if (bytes <=0)
-	    return done;
-	if (bytes<sizeof(frame)-1)
-	    fail("invalid wav gsm frame size: %d bytes",bytes);
-	if(gsm_decode(wav->gsmhandle,frame, &(wav->gsmsample[160]))<0)
+	/* decode the short 32 byte half */
+	if(gsm_decode(wav->gsmhandle,frame+33, wav->gsmsample+160)<0)
 	    fail("error during gsm decode");
 
-
 	while ((wav->gsmindex <160*2) && (done < len)){
 	    buf[done++]=LEFT(wav->gsmsample[(wav->gsmindex)++],16);
 	}
@@ -318,35 +309,50 @@
     return done;
 }
 
+static void wavgsmflush(ft, pad)
+ft_t ft;
+int pad; /* normally 0, but 1 to pad last write to even datalen */
+{
+    gsm_byte	frame[65];
+    wav_t	wav = (wav_t) ft->priv;
+
+    /* zero fill as needed */
+    while(wav->gsmindex<160*2)
+	wav->gsmsample[wav->gsmindex++]=0;
+
+    /*encode the even half short (32 byte) frame */
+    gsm_encode(wav->gsmhandle, wav->gsmsample, frame);
+    /*encode the odd half long (33 byte) frame */
+    gsm_encode(wav->gsmhandle, wav->gsmsample+160, frame+32);
+    if (fwrite(frame, 1, 65, ft->fp) != 65)
+	fail("write error");
+    wav->gsmbytecount += 65;
+
+    wav->gsmindex = 0;
+
+    if (pad & wav->gsmbytecount){
+	/* pad output to an even number of bytes */
+	if(fputc(0,ft->fp))
+	    fail("write error");
+	wav->gsmbytecount += 1;
+    }
+}
+
 void wavgsmwrite(ft, buf, len)
 ft_t ft;
 LONG *buf, len;
 {
     wav_t	wav = (wav_t) ft->priv;
-
     int done = 0;
-    gsm_frame	frame;
 
     while (done < len) {
-	while ((wav->gsmindex < 160*2) && (done < len)){
+	while ((wav->gsmindex < 160*2) && (done < len))
 	    wav->gsmsample[(wav->gsmindex)++] = RIGHT(buf[done++], 16);
-	}
-	if (wav->gsmindex < 160*2){
-	    return;
-	}
 
-	/*encode the even half and write short (32 byte) frame */
-	gsm_encode(wav->gsmhandle, wav->gsmsample, frame);
-	if (fwrite(frame, 1, sizeof(frame)-1, ft->fp) != sizeof(frame)-1)
-	    fail("write error");
-	wav->gsmbytecount += sizeof(frame)-1;
+	if (wav->gsmindex < 160*2)
+	    break;
 
-	/*encode the odd half and write long (33 byte) frame */
-	gsm_encode(wav->gsmhandle, &(wav->gsmsample[160]), frame);
-	if (fwrite(frame, 1, sizeof(frame), ft->fp) != sizeof(frame))
-	    fail("write error");
-	wav->gsmbytecount += sizeof(frame);
-	wav->gsmindex = 0;
+	wavgsmflush(ft, 0);
     }     
 
 }
@@ -354,32 +360,11 @@
 void wavgsmstopwrite(ft)
 ft_t ft;
 {
-    gsm_frame frame;
     wav_t	wav = (wav_t) ft->priv;
-    if (wav->gsmindex){
-	while(wav->gsmindex<160*2){
-	    wav->gsmsample[wav->gsmindex++]=0;
-	}
 
-	/*encode the even half and write short (32 byte) frame */
-	gsm_encode(wav->gsmhandle, wav->gsmsample, frame);
-	if (fwrite(frame, 1, sizeof(frame)-1, ft->fp) != sizeof(frame)-1)
-	    fail("write error");
-	wav->gsmbytecount += sizeof(frame)-1;
-	
-	/*encode the odd half and write long (33 byte) frame */
-	gsm_encode(wav->gsmhandle, &(wav->gsmsample[160]), frame);
-	if (fwrite(frame, 1, sizeof(frame), ft->fp) != sizeof(frame))
-	    fail("write error");
-	wav->gsmbytecount += sizeof(frame);
+    if (wav->gsmindex)
+	wavgsmflush(ft, 1);
 
-	/* pad output to an even number of bytes */
-	if (wav->gsmbytecount & 0x1){
-	    if(fputc(0,ft->fp))
-		fail("write error");
-	    wav->gsmbytecount += 1;
-	}
-    }      
     wavgsmdestroy(ft);
 }
 #endif        /*ifdef out gsm code */
@@ -848,9 +833,8 @@
 		    top = p+ct;
 		    /* Output is already signed */
 		    while (p<top)
-		    {
 			*buf++ = LEFT((*p++), 16);
-		    }
+
 		    wav->samplePtr = p;
 		}
 	    }
@@ -863,7 +847,7 @@
 		warn("Premature EOF on .wav input file");
 	break;
 #endif
-	default: /* not ADPCM style */
+	default: /* assume PCM style */
 	    done = rawread(ft, buf, len);
 	    /* If software thinks there are more samples but I/O */
 	    /* says otherwise, let the user know about this.     */
@@ -1007,7 +991,7 @@
 
 */
 
-void wavwritehdr(ft, second_header) 
+static void wavwritehdr(ft, second_header) 
 ft_t ft;
 int second_header;
 {
--- a/src/wve.c
+++ b/src/wve.c
@@ -19,7 +19,7 @@
     short repeats;
     };
 
-void wvewriteheader(P1(ft_t ft));
+static void wvewriteheader(P1(ft_t ft));
 
 void wvestartread(ft) 
 ft_t ft;
@@ -165,7 +165,7 @@
 	rawstopwrite(ft);
 }
 
-void wvewriteheader(ft)
+static void wvewriteheader(ft)
 ft_t ft;
 {
 
--- a/test/ding.c
+++ b/test/ding.c
@@ -43,14 +43,83 @@
 #endif
 
 struct _Env {
-	int r;    /* rise   */
-	int m;    /* middle */
-	int f;    /* fall   */
-	int e;    /* end     */
-	FLOAT v; /* volume */
-	FLOAT d; /* decay */
-} Env = {0,0,0,0,0.5,1.0};
+	struct _Env *next;
+	int r;        /* rise   */
+	int m;        /* middle */
+	int f;        /* fall   */
+	int e;        /* end    */
+	FLOAT frq;    /* frequency  */
+	FLOAT b;	    /* bend (not quite implemented) */
+	FLOAT v;	    /* volume */
+	FLOAT d;	    /* decay  */
+	FLOAT x,y;	  /* current x,y */
+	FLOAT phx,phy;/* per-sample phase multiplier cos(PI*frq),sin(PI*frq) */
+};
 
+const struct _Env EnvTemplate = {NULL,0,0,0,0,0.0,0.0,0.5,1.0,0.0,1.0,1.0,0.0};
+
+struct _Env * Env0 = NULL; /* 1st  */
+struct _Env * EnvL = NULL; /* last */
+
+struct _Env *env_new(struct _Env *L)
+{
+	struct _Env *E;
+	E = (struct _Env *) malloc(sizeof(struct _Env));
+	if (E) {
+		memcpy(E, (L)? L : &EnvTemplate, sizeof(struct _Env));
+	}
+	return E;
+}
+	
+static void env_init(struct _Env *E)
+{
+	if (0<E->frq && E->frq<1) {
+		E->x = 1; E->y = 0;
+	} else {
+		E->x = 0; E->y = 1;
+	}
+	E->phx = cos(E->frq*M_PI);
+	E->phy = sin(E->frq*M_PI);
+}
+	
+static void env_post(struct _Env *E, int k)
+{
+	double x1;
+	x1   = E->x*E->phx - E->y*E->phy;
+	E->y = E->x*E->phy + E->y*E->phx;
+	E->x = x1;
+	/* update (x,y) for next tic */
+	if (!(k&0x0f)) {  /* norm correction each 16 samples */
+		x1 = 1/sqrt(E->x*E->x+E->y*E->y);
+		E->x *= x1;
+		E->y *= x1;
+	}
+}
+
+/* rise/fall function, monotonic [0,1] -> [1-0] */
+static inline double ramp(double s)
+{
+	return 0.5*(1 + cos(M_PI * s));
+}
+
+static double env(struct _Env *Env, int k)
+{
+	double u = 0;
+	//if (k >= Env->r && k < Env->e) {
+		if (k<Env->m) {
+			u = Env->v * ramp((double)(Env->m-k)/(Env->m-Env->r));
+		}else if (k<Env->f) {
+			u = Env->v;
+			Env->v *= Env->d;
+		}else{
+			u = Env->v * ramp((double)(k-Env->f)/(Env->e-Env->f));
+		}
+		u *= Env->y;
+		env_post(Env,k);
+	//}
+	return u;
+}
+
 static void Usage(void)__attribute__((noreturn));
 
 static void Usage(void)
@@ -57,12 +126,10 @@
 {
 	fprintf(stderr, "Usage: ./ding [options] [<in-file>] <out-file>\n");
 	fprintf(stderr, "  Options:\n");
+	fprintf(stderr, "    -f <freq>      float, frequency = freq*nyquist_rate\n");
 	fprintf(stderr, "    [-v <vol>]     float, volume, 1.00 is max\n");
-	fprintf(stderr, "    [-f <freq>]    float, frequency = freq*nyquist_rate\n");
 	fprintf(stderr, "    [-d <decay>]   float, per-sample decay factor\n");
-	fprintf(stderr, "    [-o <n>]       int, zero-pad samples before tone\n");
-	fprintf(stderr, "    [-e <n>]       int, length in samples of tone\n");
-	fprintf(stderr, "    [-p <n>]       int, zero-pad samples after tone\n");
+	fprintf(stderr, "    [-e start:attack:duration:mute]  ints \n");
 	exit(-1);
 }
 
@@ -109,63 +176,20 @@
 	SAMPL *ibuff,max,min;
 	int poflo,moflo;
 	FLOAT Vol0=1;
-	FLOAT Freq=0;   /* of nyquist */
-	int Pad=0;
-	double x=1,y=0;
-	double thx=1,thy=0;
-				
-	static inline void a_init(double frq)
-	{
-		if (0<frq && frq<1) {
-			x = 1; y = 0;
-		} else {
-			x = 0; y = 1;
-		}
-		thx = cos(frq*M_PI);
-		thy = sin(frq*M_PI);
-	}
-	
-	static inline void a_post(int k)
-	{
-		double x1;
-		x1 = x*thx - y*thy;
-		y  = x*thy + y*thx;
-		x = x1;
-		/* update (x,y) for next tic */
-		if (!(k&0x0f)) {  /* norm correction each 16 samples */
-			x1 = 1/sqrt(x*x+y*y);
-			x *= x1;
-			y *= x1;
-		}
-	}
+	struct _Env *E;
 
-	/* rise/fall function, monotonic [0,1] -> [1-0] */
-	static inline const double a(double s)
-	{
-		return 0.5*(1 + cos(M_PI * s));
-	}
-
-	static double env(struct _Env *Env, int k)
-	{
-		double u = 0;
-		//if (k >= Env->r && k < Env->e) {
-			if (k<Env->m) {
-				u = Env->v * a((double)(Env->m-k)/(Env->m-Env->r));
-			}else if (k<Env->f) {
-				u = Env->v;
-			}else{
-				u = Env->v * a((double)(k-Env->f)/(Env->e-Env->f));
-			}
-		//}
-		return u;
-	}
-
 	 /* Parse the options */
-	while ((optc = getopt(argct, argv, "d:o:e:t:p:f:v:h")) != -1) {
+	E = NULL;
+	len = 0;
+	while ((optc = getopt(argct, argv, "d:e:f:v:h")) != -1) {
 		char *p;
 		switch(optc) {
 			case 'd':
-				Env.d = strtod(optarg,&p);
+				if (!E) {
+					fprintf(stderr,"option -f must precede -%c\n", optc);
+					Usage();
+				}
+				E->d = strtod(optarg,&p);
 				if (p==optarg || *p) {
 					fprintf(stderr,"option -%c expects float value (%s)\n", optc, optarg);
 					Usage();
@@ -172,7 +196,12 @@
 				}
 				break;
 			case 'f':
-				Freq = strtod(optarg,&p);
+				E = env_new(EnvL);
+				if (EnvL) EnvL->next = E;
+				EnvL = E;
+				if (!Env0) Env0 = E;
+
+				E->frq = strtod(optarg,&p);
 				if (p==optarg || *p) {
 					fprintf(stderr,"option -%c expects float value (%s)\n", optc, optarg);
 					Usage();
@@ -179,36 +208,47 @@
 				}
 				break;
 			case 'e':
-				p = optarg;
-				Env.f  = strtol(p,&p,10);
-				if (*p++ == ':') {
-					Env.m = Env.e = Env.f;
-					Env.f = strtol(p,&p,10);
+				{
+				int t[5], tmin=0;
+				int i,ct=0;
+
+				if (!E) {
+					fprintf(stderr,"option -f must precede -%c\n", optc);
+					Usage();
 				}
-				if (*p++ == ':') {
-					Env.e = strtol(p,&p,10);
+				for (p=optarg,ct=0; ct<5; p++) {
+					t[ct] = 0;
+					if (*p && *p != ':') { 
+						t[ct] = strtol(p,&p,10);
+						if (t[ct]<tmin) tmin=t[ct];
+					}
+					ct++;
+					if (*p != ':') break;
 				}
-				if (*p || Env.f<=0 || Env.m<0 || Env.e<0) {
+				if (ct==4) t[ct++] = 0;
+
+				if (*p || tmin<0 || ct!=5) {
 					fprintf(stderr,"option -%c not valid (%s)\n", optc, optarg);
 					Usage();
 				}
+
+				for (i=1; i<ct; i++)
+					t[i] += t[i-1];
+
+				E->r = t[0];
+				E->m = t[1];
+				E->f = t[2];
+				E->e = t[3];
+				if (len<t[4]) len=t[4];
 				break;
-			case 'o':
-				Env.r = strtol(optarg,&p,10);
-				if (p==optarg || *p || Env.r<0) {
-					fprintf(stderr,"option -%c expects int value (%s)\n", optc, optarg);
-					Usage();
 				}
-				break;
-			case 'p':
-				Pad = strtol(optarg,&p,10);
-				if (p==optarg || *p || Pad<0) {
-					fprintf(stderr,"option -%c expects int value (%s)\n", optc, optarg);
+			case 'v':
+				if (!E) {
+					fprintf(stderr,"option -f must precede -%c\n", optc);
 					Usage();
 				}
-				break;
-			case 'v':
-				Env.v = strtod(optarg,&p);
+				E->v = MAXSAMPL*strtod(optarg,&p);
+				if (E->frq==0.0) E->v *= sqrt(0.5);
 				if (p==optarg || *p) {
 					fprintf(stderr,"option -%c expects float value (%s)\n", optc, optarg);
 					Usage();
@@ -220,24 +260,20 @@
 		}
 	}
 
-	Env.v *= MAXSAMPL;
-	if (Freq==0.0) Env.v *= sqrt(0.5);
 	//fprintf(stderr,"Vol0 %8.3f\n", Vol0);
 
-	Env.m += Env.r;
-	Env.f += Env.m;
-	Env.e += Env.f;
-	len = Env.e+Pad;
 	fnam1=NULL; fd1=-1;
 	//fprintf(stderr,"optind=%d argct=%d\n",optind,argct);
 	if (optind <= argct-2) {
+		int ln1;
 		fnam1=argv[optind++];
 		fd1=open(fnam1,O_RDONLY);
 		if (fd1<0) {
 			fprintf(stderr,"Open: %s %s\n",fnam1,strerror(errno)); return(1);
 		}
-		len=lseek(fd1,0,SEEK_END)/2;
+		ln1=lseek(fd1,0,SEEK_END)/2;
 		lseek(fd1,0,SEEK_SET);
+		if (len<ln1) len = ln1;
 	}
 
 	if (optind != argct-1) Usage();
@@ -252,7 +288,7 @@
 	}
 	//fprintf(stderr, "Files: %s %s\n",fnam1,fnam2);
 
-	a_init(Freq);
+	for (E=Env0; (E); E=E->next) env_init(E);
 
 	ibuff=(SAMPL*)malloc(BSIZ*sizeof(SAMPL));
 	poflo=moflo=0;
@@ -270,9 +306,10 @@
 			else if (min>*ibp) min=*ibp;
 			v *= Vol0;
 
-			if (st>=Env.r && st<Env.e) {
-				v += y*env(&Env,st);
-				a_post(st);
+			for (E=Env0; (E); E=E->next) {
+				if (st>=E->r && st<E->e) {
+					v += env(E, st);
+				}
 			}
 
 			if (v>MAXSAMPL) {
--- a/test/ltest.pl
+++ b/test/ltest.pl
@@ -33,7 +33,7 @@
 
 my ($rate0,$rate1)=(8000,22050); # sample rates
 my $p=400;  # silence before/after tonepulse
-my $env="-e4000:16000:4000"; # attack, duration, drop
+my $env="4000:16000:4000"; # attack, duration, drop
 
 #my ($rate0,$rate1)=(22050,8000); # sample rates
 #my $p=1102;  # silence before/after tonepulse
@@ -71,7 +71,7 @@
 	}
 }
 print("#   with tone pulses from 0.00 to 0.99 percent of Nyquist\n");
-print("#   produced by $ding -f0.xx -v0.5 -o$p $env -p$p d/i0.xx.$t\n");
+print("#   produced by $ding -f0.xx -v0.5 -o$p -e$p:$env:$p d/i0.xx.$t\n");
 
 # generate the test data
 mkdir("d",0775);
@@ -83,18 +83,18 @@
 
 	#if ($f>0.995) { $f=0.999; }
 	my $s=sprintf("%4.2f",$f);
-	#print "$ding -v0.5 -o$p $env -p$p -f$s -d1.0 d/i$s.$t\n";
-	qx{$ding -v0.5 -o$p $env -p$p -f$s -d1.0 d/i$s.$t &>/dev/null};
+	#print "$ding -v0.5 -e$p:$env:$p -f$s -d1.0 d/i$s.$t\n";
+	qx{$ding -v0.5 -e$p:$env:$p -f$s -d1.0 d/i$s.$t &>/dev/null};
 	if ($ratechange==0) {
 		qx{$sox -r$rate0 d/i$s.$t -r$rate0 d/j$s.$t $effect 2>/dev/null};
-		@mod = grep {/v2max/} qx{$model -f$s $env $rate0 d/j$s.$t 2>&1};
+		@mod = grep {/v2max/} qx{$model -f$s -e$env $rate0 d/j$s.$t 2>&1};
 	} else {
 		qx{$sox -r$rate0 d/i$s.$t -r$rate1 d/u$s.$t $effect 2>/dev/null};
 		if ($updown) {
 			qx{$sox -r$rate1 d/u$s.$t -r$rate0 d/o$s.$t $effect 2>/dev/null};
-			@mod = grep {/v2max/} qx{$model -f$s $env $rate0:$rate0 d/o$s.$t 2>&1};
+			@mod = grep {/v2max/} qx{$model -f$s -e$env $rate0:$rate0 d/o$s.$t 2>&1};
 		}else{
-			@mod = grep {/v2max/} qx{$model -f$s $env $rate0:$rate1 d/u$s.$t 2>&1};
+			@mod = grep {/v2max/} qx{$model -f$s -e$env $rate0:$rate1 d/u$s.$t 2>&1};
 		}
 	}
 	print STDERR "$s: ",@mod;
--- a/test/wtest.pl
+++ b/test/wtest.pl
@@ -28,7 +28,7 @@
 
 my $rate=8000; # sample rates
 my $p=200;  # silence before/after tonepulse
-my $env="-e2000:8000:2000"; # attack, duration, drop
+my $env="-e400:800:400"; # attack, duration, drop
 
 my ($ding,$model,$t,$rms,$lim)=("./ding","./model","sw",11585.2, 0.5);
 
@@ -78,9 +78,11 @@
 		qx{$toast -l d/i$s.$t};
 		qx{$toast -dl d/i$s.$t.gsm};
 	}else{
+		qx{cp d/i$s.$t d/a$s.$t 2>/dev/null};
 		qx{$sox -r$rate d/i$s.$t -g d/g$s.wav $effect 2>/dev/null};
 		unlink "d/i$s.$t";
 		qx{$sox d/g$s.wav d/i$s.$t 2>/dev/null};
+		qx{cp d/i$s.$t d/b$s.$t 2>/dev/null};
 		unlink "d/g$s.wav";
 	}