shithub: sox

Download patch

ref: 852e497803ad66828a4ed4d34c9ebc1f919dfa4f
parent: d31c2acc4458fc4e0bab401d04997b486f373bd1
author: cbagwell <cbagwell>
date: Tue May 29 09:22:50 EDT 2001

Adding new dcshift effect.  Adding silence effect into build
processes, still needs some work on code.

--- a/Changelog
+++ b/Changelog
@@ -60,6 +60,9 @@
   o Unified the raw reading functions.  Probably slightly faster for
     most datatypes but was done to fix recording from the OSS driver.
     Control-C stopped working somewhere during the 12.17 series.
+  o Ford Prefect added a dcshift which can shift the midline amplitude
+    towards the true center.  This will allow for a greater range
+    of volume adjustments without clipping audio data.
 
 sox-12.17.1
 -----------
--- a/Makefile.dos
+++ b/Makefile.dos
@@ -17,12 +17,12 @@
 	  wav.obj wve.obj
 
 EOBJ	= avg.obj band.obj bandpass.obj breject.obj btrworth.obj chorus.obj \
-	  compand.obj copy.obj cut.obj deemphas.obj earwax.o echo.obj \
-	  echos.obj fade.obj filter.obj flanger.obj highp.obj highpass.obj \
-          lowp.obj lowpass.obj map.obj mask.obj phaser.obj pick.obj \
-	  pitch.obj pan.obj polyphase.obj rate.obj resample.obj reverb.obj \
-	  reverse.obj speed.obj split.obj stat.obj stretch.obj \
-	  swap.obj synth.obj trim.obj vibro.obj vol.obj
+	  compand.obj copy.obj cut.obj dcshift.obj deemphas.obj earwax.o \
+	  echo.obj echos.obj fade.obj filter.obj flanger.obj highp.obj \
+	  highpass.obj lowp.obj lowpass.obj map.obj mask.obj phaser.obj \
+	  pick.obj pitch.obj pan.obj polyphase.obj rate.obj resample.obj \
+	  reverb.obj reverse.obj silence.obj speed.obj split.obj stat.obj \
+	  stretch.obj swap.obj synth.obj trim.obj vibro.obj vol.obj
 
 LIBOBJS   = $(FOBJ) $(EOBJ) handlers.obj libst.obj misc.obj getopt.obj util.obj
 
--- a/Makefile.gcc
+++ b/Makefile.gcc
@@ -34,11 +34,11 @@
 	  tx16w.o voc.o wav.o wve.o
 
 EOBJ    = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
-          copy.o cut.o deemphas.o earwax.o echo.o echos.o fade.o filter.o \
-          flanger.o highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
-          phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
-          reverse.o speed.o split.o stat.o stretch.o swap.o synth.o \
-	  trim.o vibro.o vol.o
+          copy.o cut.o dcshift.o deemphas.o earwax.o echo.o echos.o fade.o \
+	  filter.o flanger.o highp.o highpass.o lowp.o lowpass.o map.o \
+	  mask.o pan.o phaser.o pick.o pitch.o polyphas.o rate.o \
+	  resample.o reverb.o reverse.o silence.o speed.o split.o \
+	  stat.o stretch.o swap.o synth.o trim.o vibro.o vol.o
 
 SOUNDLIB = libst.a
 LIBOBJS = $(FOBJ) $(EOBJ) handlers.o libst.o misc.o util.o getopt.o
--- a/TODO
+++ b/TODO
@@ -1,6 +1,8 @@
 People are encouraged to pick some of these and implement it.  Send
 all patches to cbagwell@sprynet.com.
 
+  o Add in manual page entries for silence and dcshift.
+
   o Make a global version of MIN/MAX instead of sprinkled min/max/MIN/MAX
 
   o Unify the raw writing functions to work line the raw reading functions.
--- a/libst.3
+++ b/libst.3
@@ -140,11 +140,16 @@
 .TP 20 
 flow
 is called with input and output data buffers,
-and (by reference) the input and output data sizes.
+and (by reference) the input and output data buffer sizes.
 It processes the input buffer into the output buffer,
 and sets the size variables to the numbers of samples
 actually processed.
-It is under no obligation to fill the output buffer.
+It is under no obligation to read from the input buffer or
+write to the output buffer during the same call.  If the
+call returns 0 for both input and output sizes then this will
+indicate that it no longer expects to process
+samples and the application program can start calling the drain
+operation.
 .TP 20 
 drain
 is called after there are no more input data samples.
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -48,11 +48,11 @@
 	  wav.o wve.o
 
 EOBJ	= avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
-	  copy.o cut.o deemphas.o earwax.o echo.o echos.o fade.o filter.o \
-	  flanger.o highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
-	  phaser.o pick.o pitch.o polyphas.o rate.o resample.o reverb.o \
-	  reverse.o speed.o split.o stat.o stretch.o swap.o synth.o trim.o \
-	  vibro.o vol.o
+	  copy.o cut.o dcshift.o deemphas.o earwax.o echo.o echos.o \
+	  fade.o filter.o flanger.o highp.o highpass.o lowp.o lowpass.o \
+	  map.o mask.o pan.o phaser.o pick.o pitch.o polyphas.o \
+	  rate.o resample.o reverb.o reverse.o silence.o speed.o split.o \
+	  stat.o stretch.o swap.o synth.o trim.o vibro.o vol.o
 
 OSSOBJ_0    =
 OSSOBJ_1    = oss.o
--- /dev/null
+++ b/src/dcshift.c
@@ -1,0 +1,221 @@
+/*
+ * dcshift.c
+ * (c) 2000.04.15 Chris Ausbrooks <weed@bucket.pp.ualr.edu>
+ *
+ * based on vol.c which is
+ * (c) 20/03/2000 Fabien COELHO <fabien@coelho.net> for sox.
+ *
+ * DC shift a sound file, with basic linear amplitude formula.
+ * Beware of saturations! clipping is checked and reported.
+ * Cannot handle different number of channels.
+ * Cannot handle rate change.
+ */
+
+#include "st.h"
+
+#include <math.h>   /* exp(), sqrt() */
+#include <limits.h> /* LONG_MAX */
+
+/* type used for computations. 
+ */
+#ifndef DCSHIFT_FLOAT
+#define DCSHIFT_FLOAT float
+#define DCSHIFT_FLOAT_SCAN "%f"
+#endif
+
+/* constants
+ */
+#define ZERO	  ((DCSHIFT_FLOAT)(0.0e0))
+#define LOG_10_20 ((DCSHIFT_FLOAT)(0.1151292546497022842009e0))
+#define ONE	  ((DCSHIFT_FLOAT)(1.0e0))
+#define TWENTY	  ((DCSHIFT_FLOAT)(20.0e0))
+
+#define DCSHIFT_USAGE \
+    "Usage: dcshift shift [ limitergain ]" \
+    " The peak limiter has a gain much less than 1.0 (ie 0.05 or 0.02) which is only" \
+    " used on peaks to prevent clipping. (default is no limiter)"
+
+typedef struct {
+    DCSHIFT_FLOAT dcshift; /* DC shift. */
+    
+    int uselimiter; /* boolean: are we using the limiter? */
+    DCSHIFT_FLOAT limiterthreshhold;
+    DCSHIFT_FLOAT limitergain; /* limiter gain. */
+    int limited; /* number of limited values to report. */
+    int totalprocessed;
+    
+    int clipped;    /* number of clipped values to report. */
+} * dcs_t;
+
+/*
+ * Process options: dcshift (float) type (amplitude, power, dB)
+ */
+int st_dcshift_getopts(effp, n, argv) 
+eff_t effp;
+int n;
+char **argv;
+{
+    dcs_t dcs = (dcs_t) effp->priv; 
+    dcs->dcshift = ONE; /* default is no change */
+    dcs->uselimiter = 0; /* default is no limiter */
+    
+    if (n && (!sscanf(argv[0], DCSHIFT_FLOAT_SCAN, &dcs->dcshift)))
+    {
+	st_fail(DCSHIFT_USAGE);
+	return ST_EOF;
+    }
+
+    if (n>1)
+    {
+    	if (!sscanf(argv[1], DCSHIFT_FLOAT_SCAN, &dcs->limitergain))
+    	{
+  		st_fail(DCSHIFT_USAGE);
+		return ST_EOF;  		
+    	}
+    	
+    	dcs->uselimiter = 1; /* ok, we'll use it */
+    	/* The following equation is derived so that there is no discontinuity in output amplitudes */
+    	/* and a LONG_MAX input always maps to a LONG_MAX output when the limiter is activated. */
+    	/* (NOTE: There **WILL** be a discontinuity in the slope of the output amplitudes when using the limiter.) */
+    	dcs->limiterthreshhold = LONG_MAX * (ONE - (fabs(dcs->dcshift) - dcs->limitergain));
+    }
+    
+    return ST_SUCCESS;
+}
+
+/*
+ * Start processing
+ */
+int st_dcshift_start(effp)
+eff_t effp;
+{
+    dcs_t dcs = (dcs_t) effp->priv;
+    
+    if (effp->outinfo.channels != effp->ininfo.channels)
+    {
+	st_warn("DCSHIFT cannot handle different channels (in=%d, out=%d)"
+	     " use avg or pan", effp->ininfo.channels, effp->outinfo.channels);
+    }
+
+    if (effp->outinfo.rate != effp->ininfo.rate)
+    {
+	st_fail("DCSHIFT cannot handle different rates (in=%ld, out=%ld)"
+	     " use resample or rate", effp->ininfo.rate, effp->outinfo.rate);
+	return ST_EOF;
+    }
+
+    dcs->clipped = 0;
+    dcs->limited = 0;
+    dcs->totalprocessed = 0;
+
+    return ST_SUCCESS;
+}
+
+/* conversion. clipping could be smoother at high ends?
+ * this could be a function on its own, with clip count and report
+ * handled by eff_t and caller.
+ */
+static LONG clip(dcs_t dcs, const DCSHIFT_FLOAT v)
+{
+    if (v > LONG_MAX)
+    {
+	 dcs->clipped++;
+	 return LONG_MAX;
+    }
+    else if (v < -LONG_MAX)
+    {
+	dcs->clipped++;
+	return -LONG_MAX;
+    }
+    /* else */
+    return (LONG) v;
+}
+
+#ifndef MIN
+#define MIN(s1,s2) ((s1)<(s2)?(s1):(s2))
+#endif
+
+/*
+ * Process data.
+ */
+int st_dcshift_flow(effp, ibuf, obuf, isamp, osamp)
+eff_t effp;
+LONG *ibuf, *obuf;
+LONG *isamp, *osamp;
+{
+    dcs_t dcs = (dcs_t) effp->priv;
+    register DCSHIFT_FLOAT dcshift = dcs->dcshift;
+    register DCSHIFT_FLOAT limitergain = dcs->limitergain;
+    register DCSHIFT_FLOAT limiterthreshhold = dcs->limiterthreshhold;
+    register DCSHIFT_FLOAT sample;
+    register LONG len;
+    
+    len = MIN(*osamp, *isamp);
+
+    /* report back dealt with amount. */
+    *isamp = len; *osamp = len;
+    
+    if (dcs->uselimiter)
+    {
+	dcs->totalprocessed += len;
+	
+	for (;len>0; len--)
+	    {
+	    	sample = *ibuf++;
+	    	
+	    	if (sample > limiterthreshhold && dcshift > 0)
+	    	{
+	    		sample =  (sample - limiterthreshhold) * limitergain / (LONG_MAX - limiterthreshhold) + limiterthreshhold + dcshift;
+	    		dcs->limited++;
+	    	}
+	    	else if (sample < -limiterthreshhold && dcshift < 0)
+	    	{
+	    		sample =  (sample + limiterthreshhold) * limitergain / (LONG_MAX - limiterthreshhold) - limiterthreshhold + dcshift;
+	    		dcs->limited++;
+	    	}
+	    	else
+	    	{
+	    		sample = dcshift * LONG_MAX + sample;
+	    	}
+
+		*obuf++ = clip(dcs, sample);
+	    }
+    }
+    else
+    {
+    	/* quite basic, with clipping */
+    	for (;len>0; len--)
+		*obuf++ = clip(dcs, dcshift * LONG_MAX + *ibuf++);
+    }
+    return ST_SUCCESS;
+}
+
+/*
+ * Do anything required when you stop reading samples.  
+ * Don't close input file! 
+ */
+int st_dcshift_stop(effp)
+eff_t effp;
+{
+    dcs_t dcs = (dcs_t) effp->priv;
+
+    if (dcs->limited)
+    {
+	st_warn("DCSHIFT limited %d values (%d percent).", 
+	     dcs->limited, (int) (dcs->limited * 100.0 / dcs->totalprocessed));
+    }
+    if (dcs->clipped) 
+    {
+    	if (dcs->dcshift > 0)
+    	{
+	     st_warn("DCSHIFT clipped %d values, dcshift=%f too high...", 
+	          dcs->clipped, dcs->dcshift);
+	}
+    	else
+    	{
+	     st_warn("DCSHIFT clipped %d values, dcshift=%f too low...", 
+	          dcs->clipped, dcs->dcshift);
+	}
+    }
+    return ST_SUCCESS;
+}
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -534,6 +534,11 @@
 extern int st_cut_flow();
 extern int st_cut_stop();
 
+extern int st_dcshift_getopts();
+extern int st_dcshift_start();
+extern int st_dcshift_flow();
+extern int st_dcshift_stop();
+
 extern int st_deemph_getopts();
 extern int st_deemph_start();
 extern int st_deemph_flow();
@@ -649,6 +654,12 @@
 extern int st_reverse_drain();
 extern int st_reverse_stop();
 
+extern int st_silence_getopts();
+extern int st_silence_start();
+extern int st_silence_flow();
+extern int st_silence_drain();
+extern int st_silence_stop();
+
 extern int st_speed_getopts();
 extern int st_speed_start();
 extern int st_speed_flow();
@@ -734,6 +745,9 @@
 	{"cut", ST_EFF_MCHAN, 
 		st_cut_getopts, st_cut_start, st_cut_flow, 
 		st_null_drain, st_nothing},
+	{"dcshift", ST_EFF_MCHAN, 
+		st_dcshift_getopts, st_dcshift_start, st_dcshift_flow, 
+		st_null_drain, st_dcshift_stop},
 	{"deemph", ST_EFF_MCHAN,
 	        st_deemph_getopts, st_deemph_start, st_deemph_flow,
 	        st_null_drain, st_deemph_stop},
@@ -800,6 +814,9 @@
 	{"reverse", 0, 
 		st_reverse_getopts, st_reverse_start, 
 		st_reverse_flow, st_reverse_drain, st_reverse_stop},
+	{"silence", 0, 
+		st_silence_getopts, st_silence_start, 
+		st_silence_flow, st_silence_drain, st_silence_stop},
 	{"speed", 0, 
 		st_speed_getopts, st_speed_start, 
 		st_speed_flow, st_speed_drain, st_speed_stop},
--- a/src/silence.c
+++ b/src/silence.c
@@ -1,40 +1,53 @@
+/*	Silence effect for SoX
+ *	by Heikki Leinonen (heilei@iki.fi) 25.03.2001
+ *
+ *	This effect deletes samples from the start of the sound
+ *	file until a sample exceeds a given threshold (either
+ *	left or right channel in stereo files). This can be used
+ *	to filter out unwanted silence or low noise in the beginning
+ *	of a sound file. The threshold can be given either as a
+ *	percentage or in decibels.
+ */
 
-//	Silence effect for SoX
-//	by Heikki Leinonen (heilei@iki.fi) 25.03.2001
-//
-//	This effect deletes samples from the start of the sound
-//	file until a sample exceeds a given threshold (either
-//	left or right channel in stereo files). This can be used
-//	to filter out unwanted silence or low noise in the beginning
-//	of a sound file. The threshold can be given either as a
-//	percentage or in decibels.
 
-
+#include <string.h>
 #include <math.h>
 #include "st.h"
 
-#ifndef min
-	#define min(s1,s2) ((s1)<(s2)?(s1):(s2))
+#ifndef TRUE
+#define TRUE 1
 #endif
 
-#ifndef true
-	#define	true	1
+#ifndef FALSE
+#define FALSE 0
 #endif
 
-#ifndef false
-	#define	false	0
+#ifndef min
+#define min(s1,s2) ((s1)<(s2)?(s1):(s2))
 #endif
 
-//	Private data for silence effect.
+/* Private data for silence effect. */
 
-#define	DEFAULT_THRESHOLD	1.0
-#define	DEFAULT_UNIT		'%'
+#define SILENCE_START  0
+#define SILENCE_TRIM   1 
+#define SILENCE_COPY   2
+#define SILENCE_FLUSH  3
+#define SILENCE_STOP   4
 
 typedef struct silencestuff
 {
-	double	threshold;
-	char	unit;	//	"d" for decibels or "%" for percent.
-	int		wentAboveThreshold;
+    char	trim;
+    double	trim_threshold;
+    char	trim_unit; /* "d" for decibels or "%" for percent. */
+    char	stop;
+    double	stop_threshold;
+    ULONG	stop_duration;
+    char	stop_unit;
+    LONG	*holdoff;
+    ULONG	holdoff_count;
+    ULONG	holdoff_offset;
+    char	mode;
+    char	crossings;
 } *silence_t;
 
 
@@ -42,27 +55,27 @@
 {
 	silence_t	silence = (silence_t) effp->priv;
 
-	silence->threshold = DEFAULT_THRESHOLD;
-	silence->unit = DEFAULT_UNIT;
 	switch (n)
 	{
-		case 0:	//	No arguments, use defaults given above.
+		case 0:	/* No arguments, use defaults given above. */
 		break;
 
 		case 1:
-			sscanf(argv[0], "%lf", &silence->threshold);
+			sscanf(argv[0], "%lf", &silence->trim_threshold);
 		break;
 
 		default:
-			sscanf(argv[0], "%lf", &silence->threshold);
-			sscanf(argv[1], "%c", &silence->unit);
+			sscanf(argv[0], "%lf", &silence->trim_threshold);
+			sscanf(argv[1], "%c", &silence->trim_unit);
 		break;
 	}
-	if ((silence->unit != '%') && (silence->unit != 'd'))
+	/* silence threshold type duration count [-notrim ] [ threshold type duration count ] */
+	if ((silence->trim_unit != '%') && (silence->trim_unit != 'd'))
 		st_fail("Usage: silence [threshold [d | %%]]");
-	if ((silence->unit == '%') && ((silence->threshold < 0.0) || (silence->threshold > 100.0)))
+	if ((silence->trim_unit == '%') && ((silence->trim_threshold < 0.0) || 
+		                             (silence->trim_threshold > 100.0)))
 		st_fail("silence threshold should be between 0.0 and 100.0 %%");
-	if ((silence->unit == 'd') && (silence->threshold >= 0.0))
+	if ((silence->trim_unit == 'd') && (silence->trim_threshold >= 0.0))
 		st_fail("silence threshold should be less than 0.0 dB");
 	return(ST_SUCCESS);
 }
@@ -71,8 +84,22 @@
 {
 	silence_t	silence = (silence_t) effp->priv;
 
-	silence->wentAboveThreshold = false;
+	silence->trim = TRUE;
+	silence->trim_threshold = 1.0;
+	silence->trim_unit = '%';
 
+	silence->stop = TRUE;
+	silence->stop_threshold = 1.0;
+	silence->stop_duration = 1.8 * effp->ininfo.rate;
+	silence->stop_unit = '%';
+	silence->holdoff = malloc(sizeof(LONG)*silence->stop_duration);
+	silence->holdoff_count = 0;
+	silence->holdoff_offset = 0;
+
+	silence->mode = SILENCE_START;
+
+	silence->crossings = 0;
+
 	if ((effp->outinfo.channels != 1) && (effp->outinfo.channels != 2))
 	{
 		st_fail("Silence effect can only be run on mono or stereo data");
@@ -83,68 +110,150 @@
 
 int aboveThreshold(LONG value, double threshold, char unit)
 {
-	double	maxLong = 2147483647.0;
-	double	ratio, percentRatio, decibelRatio;
+	double	ratio;
 
-	ratio = (double) labs(value) / maxLong;
-	percentRatio = ratio * 100.0;
-	decibelRatio = log10(ratio) * 20.0;
-	return((unit == '%') ? (percentRatio >= threshold) : (decibelRatio >= threshold));
+	ratio = (double)labs(value) / (double)MAXLONG;
+	return((unit == '%') ? ((ratio * 100.0) >= threshold) : ((log10(ratio) * 20.0) >= threshold));
 }
 
-//	Process signed long samples from ibuf to obuf.
-//	Return number of samples processed in isamp and osamp.
+/* Process signed long samples from ibuf to obuf. */
+/* Return number of samples processed in isamp and osamp. */
 int st_silence_flow(eff_t effp, LONG *ibuf, LONG *obuf, LONG *isamp, LONG *osamp)
 {
-	silence_t	silence = (silence_t) effp->priv;
-	int			nrOfTicks, i;
-	LONG		leftSample, rightSample, monoSample, nrOfInSamplesRead, nrOfOutSamplesWritten;
+    silence_t silence = (silence_t) effp->priv;
+    int	threshold, i, j;
+    LONG nrOfTicks, nrOfInSamplesRead, nrOfOutSamplesWritten;
 
-	nrOfInSamplesRead = 0;
-	nrOfOutSamplesWritten = 0;
+    nrOfInSamplesRead = 0;
+    nrOfOutSamplesWritten = 0;
 
-	switch (effp->outinfo.channels)
-	{
-	case 1:
-		nrOfTicks = min((*isamp), (*osamp));
-		for(i = 0; i < nrOfTicks; i++)
+    switch (silence->mode)
+    {
+	case SILENCE_START:
+	    /* Fall through until start is written */
+	    silence->mode = SILENCE_TRIM;
+
+        /* Reads and discards all input data until it detects a
+         * sample that is above the specified threshold.  Turns on
+	 * copy mode when detected.
+	 */
+	case SILENCE_TRIM:
+	    nrOfTicks = min((*isamp), (*osamp)) / effp->ininfo.channels;
+	    for(i = 0; i < nrOfTicks; i++)
+	    {
+		threshold = 1;
+		for (j = 0; j < effp->ininfo.channels; j++)
 		{
-			monoSample = ibuf[0];
-			if (silence->wentAboveThreshold || aboveThreshold(monoSample, silence->threshold, silence->unit))
-			{
-				silence->wentAboveThreshold = true;
-				obuf[0] = ibuf[0];	//	Copy data from input to output.
-				obuf++;			//	Advance output buffers by 1 sample.
-				nrOfOutSamplesWritten++;
-			}
-			ibuf++;	//	Always advance input buffers by 1 sample.
-			nrOfInSamplesRead++;
+		    threshold &= aboveThreshold(ibuf[j], silence->trim_threshold, 
+			                        silence->trim_unit);
 		}
-	break;
-
-	case 2:
-		nrOfTicks = min((*isamp), (*osamp)) / 2;
-		for(i = 0; i < nrOfTicks; i++)
+		if (threshold)
 		{
-			leftSample = ibuf[0];
-			rightSample = ibuf[1];
-			if (silence->wentAboveThreshold || aboveThreshold(leftSample, silence->threshold, silence->unit) || aboveThreshold(rightSample, silence->threshold, silence->unit))
+		    silence->mode = SILENCE_COPY;
+		    goto silence_copy;
+		}
+		ibuf += effp->ininfo.channels;
+		nrOfInSamplesRead += effp->ininfo.channels;
+	    }
+	    break;
+	    /* Attempts to copy samples into output buffer.  If not
+	     * looking for silence to terminate copy then blindly
+	     * copy data into output buffer.
+	     *
+	     * If looking for silence, then see if input sample is above
+	     * threshold.  If found then flush out hold off buffer
+	     * and copy over to output buffer.  Tell user about
+	     * input and output processing.
+	     *
+	     * If not above threshold then store in hold off buffer
+	     * and do not write to output buffer.  Tell user input
+	     * was processed.
+	     *
+	     * If hold off buffer is full then stop copying data and
+	     * discard data in hold off buffer.
+	     */
+	case SILENCE_COPY:
+silence_copy:
+	    nrOfTicks = min((*isamp-nrOfInSamplesRead), 
+	                    (*osamp-nrOfOutSamplesWritten)) / effp->ininfo.channels;
+	    if (silence->stop)
+	    {
+	        for(i = 0; i < nrOfTicks; i++)
+	        {
+		    threshold = 0;
+		    for (j = 0; j < effp->ininfo.channels; j++)
+		    {
+		        threshold |= aboveThreshold(ibuf[j], 
+				                    silence->trim_threshold, 
+			                            silence->trim_unit);
+		    }
+		    if (threshold && silence->holdoff_count)
+		    {
+			silence->mode = SILENCE_FLUSH;
+			goto silence_flush;
+		    }
+		    else if (threshold)
+		    {
+			/* Not holding off so copy into output buffer */
+			memcpy(obuf,ibuf,sizeof(LONG)*effp->ininfo.channels);
+			nrOfInSamplesRead += effp->ininfo.channels;
+			nrOfOutSamplesWritten += effp->ininfo.channels;
+		    }
+		    else if (!threshold)
+		    {
+			/* Add to holdoff buffer */
+		        for (j = 0; j < effp->ininfo.channels; j++)
+		        {
+			    silence->holdoff[silence->holdoff_count++] = 
+				*ibuf++;
+			    nrOfInSamplesRead++;
+		        }
+			/* Check if holdoff buffer is greater than duration, 
+			 * if so then stop processing.
+			 */
+			if (silence->holdoff_count >= 
+				silence->stop_duration)
 			{
-				silence->wentAboveThreshold = true;
-				obuf[0] = ibuf[0];	//	Copy data from input to output.
-				obuf[1] = ibuf[1];
-				obuf += 2;			//	Advance output buffers by 2 samples.
-				nrOfOutSamplesWritten += 2;
+			    silence->mode = SILENCE_STOP;
+			    silence->holdoff_count = 0;
+			    break;
 			}
-			ibuf += 2;	//	Always advance input buffers by 2 samples.
-			nrOfInSamplesRead += 2;
-		}
-	break;
+		    }
+	        }
+	    }
+	    else
+	    {
+	        memcpy(obuf, ibuf, sizeof(LONG)*nrOfTicks);
+	        nrOfInSamplesRead += nrOfTicks;
+	        nrOfOutSamplesWritten += nrOfTicks;
+	    }
+	    break;
 
-	default:	//	We should never get here, but just in case...
-		st_fail("Silence effect can only be run on mono or stereo data");
-	break;
+	case SILENCE_FLUSH:
+silence_flush:
+	    nrOfTicks = min((silence->holdoff_count - silence->holdoff_offset), 
+	                    (*osamp-nrOfOutSamplesWritten)) / effp->ininfo.channels;
+	    for(i = 0; i < nrOfTicks; i++)
+	    {
+		*obuf++ = silence->holdoff[silence->holdoff_offset++];
+		nrOfOutSamplesWritten++;
+	    }
+
+	    if (silence->holdoff_offset == silence->holdoff_count)
+	    {
+		silence->holdoff_offset = 0;
+		silence->holdoff_count = 0;
+		silence->mode = SILENCE_COPY;
+		/* Return to copy mode incase there are is more room in output buffer
+		 * to copy some more data from input buffer.
+		 */
+		goto silence_copy;
+	    }
+	    break;
+	case SILENCE_STOP:
+	    break;
 	}
+
 	*isamp = nrOfInSamplesRead;
 	*osamp = nrOfOutSamplesWritten;
 
@@ -153,11 +262,39 @@
 
 int st_silence_drain(eff_t effp, LONG *obuf, LONG *osamp)
 {
-	*osamp = 0;
-	return(ST_SUCCESS);
+    silence_t silence = (silence_t) effp->priv;
+    int i;
+    LONG nrOfTicks, nrOfOutSamplesWritten = 0;
+
+    /* Only if in flush mode will there be possible samples to write
+     * out during drain() call.
+     */
+    if (silence->mode == SILENCE_FLUSH)
+    {
+        nrOfTicks = min((silence->holdoff_count - silence->holdoff_offset), 
+	                *osamp) / effp->ininfo.channels;
+	for(i = 0; i < nrOfTicks; i++)
+	{
+	    *obuf++ = silence->holdoff[silence->holdoff_offset++];
+	    nrOfOutSamplesWritten++;
+        }
+
+	if (silence->holdoff_offset == silence->holdoff_count)
+	{
+	    silence->holdoff_offset = 0;
+	    silence->holdoff_count = 0;
+	    silence->mode = SILENCE_STOP;
+	}
+    }
+
+    *osamp = nrOfOutSamplesWritten;
+    return(ST_SUCCESS);
 }
 
 int st_silence_stop(eff_t effp)
 {
-	return(ST_SUCCESS);
+    silence_t silence = (silence_t) effp->priv;
+
+    free(silence->holdoff);
+    return(ST_SUCCESS);
 }
--- a/src/sox.c
+++ b/src/sox.c
@@ -781,7 +781,7 @@
 	if (haschan > 1)
 	    st_fail("Can not specify multiple effects that modify channel #");
 	if (hasrate > 1)
-	    st_fail("Can not specify multiple effects that change sampel rate");
+	    st_fail("Can not specify multiple effects that change sample rate");
 	if (haschan && !needchan)
 	    st_fail("Can not specify channel effects when input and output channel # are equal");
 	if (hasrate && !needrate)
@@ -907,7 +907,7 @@
 	    neffects++;
         }
 
-	/* If code up until know still hasn't added avg effect then
+	/* If code up until now still hasn't added avg effect then
 	 * do it now.
 	 */
 	if (needchan && !(effects_mask & ST_EFF_CHAN))
--- a/src/stat.c
+++ b/src/stat.c
@@ -22,7 +22,7 @@
 
 /* Private data for STAT effect */
 typedef struct statstuff {
-	double	min, max;
+	double	min, max, mid;
 	double	asum;
 	double	sum1, sum2;	/* amplitudes */
 	double	dmin, dmax;
@@ -113,7 +113,7 @@
 	int i;
 	LONG  bitmask;
 
-	stat->min = stat->max = 0;
+	stat->min = stat->max = stat->mid = 0;
 	stat->asum = 0;
 	stat->sum1 = stat->sum2 = 0;
 
@@ -183,7 +183,7 @@
 	if (len==0) return (ST_SUCCESS);
 
 	if (stat->read == 0)	/* 1st sample */
-		stat->min = stat->max = stat->last = (*ibuf)/stat->scale;
+		stat->min = stat->max = stat->mid = stat->last = (*ibuf)/stat->scale;
 
 	if (stat->fft)
 	{
@@ -242,6 +242,7 @@
 			stat->min = samp;
 		else if (stat->max < samp)
 			stat->max = samp;
+		stat->mid = stat->min / 2 + stat->max / 2;
 
 		stat->sum1 += samp;
 		stat->sum2 += samp*samp;
@@ -334,6 +335,7 @@
 		f = 1.0/rms;
 		stat->max *= f;
 		stat->min *= f;
+		stat->mid *= f;
 		stat->asum *= f;
 		stat->sum1 *= f;
 		stat->sum2 *= f*f;
@@ -367,6 +369,7 @@
 		fprintf(stderr, "Scaled by:         %12.1f\n", scale);
 	fprintf(stderr, "Maximum amplitude: %12.6f\n", stat->max);
 	fprintf(stderr, "Minimum amplitude: %12.6f\n", stat->min);
+	fprintf(stderr, "Midline amplitude: %12.6f\n", stat->mid);
 	fprintf(stderr, "Mean    norm:      %12.6f\n", stat->asum/ct);
 	fprintf(stderr, "Mean    amplitude: %12.6f\n", stat->sum1/ct);
 	fprintf(stderr, "RMS     amplitude: %12.6f\n", sqrt(stat->sum2/ct));