shithub: sox

Download patch

ref: 98c0f2834f32b28ca0582aeef91fc602a9ea6517
parent: 689b86e5ae687e7c484fc87a508dae47125ec47a
author: cbagwell <cbagwell>
date: Mon Mar 27 14:13:20 EST 2000

Added pan and vol effects.

--- a/Makefile.dos
+++ b/Makefile.dos
@@ -18,9 +18,9 @@
 EOBJ	= avg.obj band.obj bandpass.obj breject.obj btrworth.obj chorus.obj \
 	  compand.obj copy.obj cut.obj deemphas.obj echo.obj \
 	  echos.obj filter.obj flanger.obj highp.obj highpass.obj lowp.obj \
-          lowpass.obj map.obj mask.obj phaser.obj pick.obj polyphas.obj \
-	  rate.obj resample.obj reverb.obj reverse.obj split.obj \
-	  stat.obj swap.obj vibro.obj
+          lowpass.obj map.obj mask.obj phaser.obj pick.obj pan.o \
+	  polyphase.obj rate.obj resample.obj reverb.obj \
+	  reverse.obj split.obj stat.obj swap.obj vibro.obj vol.obj
 
 LIBOBJS   = $(FOBJ) $(EOBJ) handlers.obj libst.obj misc.obj getopt.obj util.obj
 
--- a/Makefile.gcc
+++ b/Makefile.gcc
@@ -35,9 +35,9 @@
 
 EOBJ    = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
           copy.o cut.o deemphas.o echo.o echos.o filter.o flanger.o \
-          highp.o highpass.o lowp.o lowpass.o map.o mask.o phaser.o pick.o \
-          polyphas.o rate.o resample.o reverb.o reverse.o split.o \
-          stat.o swap.o vibro.o
+          highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
+          phaser.o pick.o polyphas.o rate.o resample.o reverb.o \
+          reverse.o split.o stat.o swap.o vibro.o vol.o
 
 SOUNDLIB = libst.a
 LIBOBJS = $(FOBJ) $(EOBJ) handlers.o libst.o misc.o util.o getopt.o
--- a/Makefile.os9
+++ b/Makefile.os9
@@ -27,9 +27,11 @@
 	  $(RDIR)/dyn.r $(RDIR)/echo.r $(RDIR)/echos.r $(RDIR)/filter.r \
 	  $(RDIR)/flanger.r \
 	  $(RDIR)/highp.r $(RDIR)/lowp.r $(RDIR)/map.r $(RDIR)/mask.r \
-	  $(RDIR)/phaser.r $(RDIR)/pick.r $(RDIR)/polyphas.r $(RDIR)/rate.r \
+	  $(RDIR)/pan.r $(RDIR)/phaser.r $(RDIR)/pick.r \
+	  $(RDIR)/polyphas.r $(RDIR)/rate.r \
 	  $(RDIR)/resample.r $(RDIR)/reverb.r $(RDIR)/reverse.r \
-	  $(RDIR)/split.r $(RDIR)/stat.r $(RDIR)/swap.r $(RDIR)/vibro.r
+	  $(RDIR)/split.r $(RDIR)/stat.r $(RDIR)/swap.r $(RDIR)/vibro.r \
+	  $(RDIR)/vol.r
 
 
 LIBOBJS	=	$(FOBJ) $(EOBJ) $(RDIR)/handlers.r $(RDIR)/libst.r \
--- a/sox.1
+++ b/sox.1
@@ -89,6 +89,8 @@
 .br
 	mask
 .br
+	pan \fIdirection\fB
+.br
 	phaser \fIgain-in gain-out delay decay speed -s\fB | \fI-t\fB
 .br
 	pick
@@ -112,6 +114,8 @@
 	swap [ \fI1 2 3 4\fB ]
 .br
 	vibro \fIspeed \fB[ \fIdepth\fB ]
+.br
+	vol \fIgain \fB[ \fItype\fB ]
 .SH DESCRIPTION
 .I SoX
 is a command line program that can convert most popular audio files
@@ -674,6 +678,19 @@
 It adds 1/2 bit of noise to the sound file at the
 output bit depth.
 .TP 10
+pan \fIdirection\fB
+Pan the sound of an audio file from one channel to another.  This is done by
+changing the volume of the input channels so that it fade's out on one
+channel and fades-in on another.  If the number of input channels is
+different then the number of output channels then this effect tries to
+intellegently handle this.  For instance, if the input contains 1 channel
+and the output contains 2 channels, then it will create the missing channel
+itself.  The 
+.I direction
+is a value from -1.0 to 1.0.  -1.0 represents
+far left and 1.0 represents far right.  Numbers in between will start the
+pan effect without totally muting the opposite channel.
+.TP 10
 phaser \fIgain-in gain-out delay decay speed -s \fR| \fI-t
 Add a phaser to a sound sample.  Each triple
 delay/decay/speed gives the delay in milliseconds
@@ -738,7 +755,7 @@
 If you are wondering which of
 .B SoX's
 rate changing effects to use, you will want to read a
-detailed analysis of all of them at http://eakaw2.et.tu-dresden.de/~andreas/resample/resample.html
+detailed analysis of all of them at http://eakaw2.et.tu-dresden.de/~wilde/resample/resample.html
 [Nov,1999: These tests need to be updated for sox-12.17, which has bugfixes to the
 resample and polyphase code.]
 .TP 10
@@ -873,6 +890,26 @@
 gives the amount the volume is cut into
 by the sine wave,
 ranging 0.0 to 1.0 and defaulting to 0.5.
+.TP 10
+vol \fIgain \fB [ \fItype\fB ]
+The vol effect is much like the command line option -v.  It allows you to
+adjust the volume of an input file and allows you to specify the adjustment
+in relation to amplitude, power, or dB.  When type is 
+.I amplitude
+then a linear change of the amplitude is performed based on the gain.  Therefore,
+a value of 1.0 will keep the volume the same, 0.0 to < 1.0 will cause the
+volume to decrease and values of > 1.0 will cause the volume to increase.
+Beware of clipping audio data when the gain is greater then 1.0.  A negative
+value performs the same adjustment while also changing the phase.
+.br
+When type is 
+.I power
+then a value of 1.0 also means no change in volume.
+.br
+When type is 
+.I dB
+the amplitude is change logrithmically.
+0.0 is constant while +6 doubles the amplitude.
 .P
 .I Sox
 enforces certain effects.
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -47,9 +47,9 @@
 
 EOBJ	= avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
 	  copy.o cut.o deemphas.o echo.o echos.o filter.o flanger.o \
-	  highp.o highpass.o lowp.o lowpass.o map.o mask.o phaser.o pick.o \
-	  polyphas.o rate.o resample.o reverb.o reverse.o split.o \
-	  stat.o swap.o vibro.o
+	  highp.o highpass.o lowp.o lowpass.o map.o mask.o pan.o \
+	  phaser.o pick.o polyphas.o rate.o resample.o reverb.o \
+	  reverse.o split.o stat.o swap.o vibro.o vol.o
 
 OSSOBJ_0    =
 OSSOBJ_1    = oss.o
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -536,6 +536,11 @@
 extern int st_mask_getopts();
 extern int st_mask_flow();
 
+extern int st_pan_getopts();
+extern int st_pan_start();
+extern int st_pan_flow();
+extern int st_pan_stop();
+
 extern int st_phaser_getopts();
 extern int st_phaser_start();
 extern int st_phaser_flow();
@@ -597,6 +602,12 @@
 extern int st_vibro_flow();
 extern int st_vibro_stop();
 
+extern int st_vol_getopts();
+extern int st_vol_start();
+extern int st_vol_flow();
+extern int st_vol_stop();
+
+
 /*
  * EFF_CHAN means that the number of channels can change.
  * EFF_RATE means that the sample rate can change.
@@ -667,6 +678,9 @@
 	{"mask", ST_EFF_MCHAN, 
 		st_mask_getopts, st_nothing, st_mask_flow, 
 		st_null_drain, st_nothing},
+	{"pan", ST_EFF_MCHAN|ST_EFF_CHAN, 
+		st_pan_getopts, st_pan_start, st_pan_flow, 
+		st_null_drain, st_pan_stop},
 	{"phaser", 0,
 	        st_phaser_getopts, st_phaser_start, st_phaser_flow,
 	        st_phaser_drain, st_phaser_stop},
@@ -700,6 +714,9 @@
 	{"vibro", 0, 
 		st_vibro_getopts, st_vibro_start, st_vibro_flow, 
 		st_null_drain, st_nothing},
+	{"vol", 0, 
+		st_vol_getopts, st_vol_start, st_vol_flow, 
+		st_null_drain, st_vol_stop},
 	{0, 0, 0, 0, 0, 0, 0}
 };
 
--- /dev/null
+++ b/src/pan.c
@@ -1,0 +1,412 @@
+/*
+ * (c) 20/03/2000 Fabien COELHO <fabien@coelho.net> for sox.
+ *
+ * Same copyright as SOX. See Sox (and Sun?;-)
+ *
+ * Change panorama of sound file with basic linear volume interpolation.
+ * The human ear is not sensible to phases? What about delay? too short?
+ * 
+ * Volume is kept constant (?). 
+ * Beware of saturations!
+ * Operations are carried out on floats.
+ * Can handle different number of channels.
+ * Cannot handle rate change.
+ *
+ * Initially based on avg effect. 
+ * pan 0.0 basically behaves as avg.
+ */
+
+#include "st.h"
+
+/* should be taken care in st.h? */
+#include <limits.h> /* LONG_MAX */
+
+/* type for computations.
+   float mantissa is okay for 8 or 16 bits signals.
+   maybe a little bit short for 24 bits.
+   switch to double if you're not happy.
+ */
+#ifndef PAN_FLOAT
+#define PAN_FLOAT float /* float or double */
+#define PAN_FLOAT_SCAN "%f" /* "%f" or "%lf" */
+#endif
+
+/* constants */
+
+#define TWO      ((PAN_FLOAT)(2.0e0))
+#define ONEHALF  ((PAN_FLOAT)(1.5e0))
+#define ONE      ((PAN_FLOAT)(1.0e0))
+#define MONE     ((PAN_FLOAT)(-1.0e0))
+#define HALF     ((PAN_FLOAT)(0.5e0))
+#define QUARTER  ((PAN_FLOAT)(0.25e0))
+#define ZERO     ((PAN_FLOAT)(0.0e0))
+
+/* error message */
+
+#define PAN_USAGE "Usage: pan direction (in [-1.0 .. 1.0])"
+
+/* structure to hold pan parameter */
+
+typedef struct {
+    /* pan option
+     */
+    PAN_FLOAT dir; /* direction, from left (-1.0) to right (1.0) */
+
+    /* local data
+     */
+    int clipped;   /* number of clipped values */
+} * pan_t;
+
+/*
+ * Process options
+ */
+int st_pan_getopts(effp, n, argv) 
+eff_t effp;
+int n;
+char **argv;
+{
+    pan_t pan = (pan_t) effp->priv; 
+    
+    pan->dir = ZERO; /* default is no change */
+    pan->clipped = 0;
+    
+    if (n && (!sscanf(argv[0], PAN_FLOAT_SCAN, &pan->dir) || 
+	      pan->dir < MONE || pan->dir > ONE))
+    {
+	fail(PAN_USAGE);
+	return ST_EOF;
+    }
+
+    return ST_SUCCESS;
+}
+
+/*
+ * Start processing
+ */
+int st_pan_start(effp)
+eff_t effp;
+{
+    if (effp->outinfo.channels==1)
+	warn("PAN onto a mono channel...");
+
+    if (effp->outinfo.rate != effp->ininfo.rate)
+    {
+	fail("PAN cannot handle different rates (in=%ld, out=%ld)"
+	     " use resample or rate", effp->ininfo.rate, effp->outinfo.rate);
+	return ST_EOF;
+    }
+
+    return ST_SUCCESS;
+}
+
+
+/* clip value if necessary. see comments about such a function in vol.c
+ * Okay, it might be quite slow to have such a function for so small
+ * a task. Hopefully the function can be inlined by the compiler?
+ */
+static LONG clip(pan_t pan, PAN_FLOAT value)
+{
+    if (value < -LONG_MAX) 
+    {
+	pan->clipped++;
+	return -LONG_MAX;
+    }
+    else if (value > LONG_MAX) 
+    {
+	pan->clipped++;
+	return LONG_MAX;
+    } /* else */
+
+    return (LONG) value;
+}
+
+#ifndef MIN
+#define MIN(s1,s2) ((s1)<(s2)?(s1):(s2))
+#endif
+
+#define UNEXPECTED_CHANNELS \
+    fail("unexpected number of channels (in=%d, out=%d)", ich, och); \
+    return ST_EOF
+
+/*
+ * Process either isamp or osamp samples, whichever is smaller.
+ */
+int st_pan_flow(effp, ibuf, obuf, isamp, osamp)
+eff_t effp;
+LONG *ibuf, *obuf;
+LONG *isamp, *osamp;
+{
+    pan_t pan = (pan_t) effp->priv;
+    register LONG len;
+    register int done, ich, och;
+    register PAN_FLOAT left, right, dir, hdir;
+    
+    dir   = pan->dir;    /* -1   <=  dir  <= 1   */
+    hdir  = HALF * dir;  /* -0.5 <=  hdir <= 0.5 */
+    left  = HALF - hdir; /*  0   <=  left <= 1   */
+    right = HALF + hdir; /*  0   <= right <= 1   */
+
+    ich = effp->ininfo.channels;
+    och = effp->outinfo.channels;
+
+    len = MIN(*osamp/och,*isamp/ich);
+
+    /* report back how much is processed. */
+    *isamp = len*ich;
+    *osamp = len*och;
+    
+    /* 9 different cases to handle: (1,2,4) X (1,2,4) */
+    switch (och) {
+    case 1: /* pan on mono channel... not much sense. just avg. */
+	switch (ich) {
+	case 1: /* simple copy */
+	    for (done=0; done<len; done++)
+		*obuf++ = *ibuf++;
+	    break;
+	case 2: /* average 2 */
+	    for (done=0; done<len; done++)
+		*obuf++ = clip(pan, HALF*ibuf[0] + HALF*ibuf[1]),
+		    ibuf += 2;
+	    break;
+	case 4: /* average 4 */
+	    for (done=0; done<len; done++)
+		*obuf++ = clip(pan, 
+			       QUARTER*ibuf[0] + QUARTER*ibuf[1] + 
+			       QUARTER*ibuf[2] + QUARTER*ibuf[3]),
+		    ibuf += 4;
+	    break;
+	default:
+	    UNEXPECTED_CHANNELS;
+	    break;
+	} /* end first switch in channel */
+	break;
+    case 2:
+	switch (ich) {
+	case 1: /* linear */
+	    for (done=0; done<len; done++)
+	    {
+		obuf[0] = clip(pan, left * ibuf[0]);
+		obuf[1] = clip(pan, right * ibuf[0]);
+		obuf += 2;
+		ibuf++;
+	    }
+	    break;
+	case 2: /* linear panorama. 
+		 * I'm not sure this is the right way to do it.
+		 */
+	    if (dir <= ZERO) /* to the left */
+	    {
+		register PAN_FLOAT volume, cll, clr, cr;
+
+		volume = ONE - HALF*dir;
+		cll = volume*(ONEHALF-left);
+		clr = volume*(left-HALF);
+		cr  = volume*(ONE+dir);
+
+		for (done=0; done<len; done++)
+		{
+		    obuf[0] = clip(pan, cll * ibuf[0] + clr * ibuf[1]);
+		    obuf[1] = clip(pan, cr * ibuf[1]);
+		    obuf += 2;
+		    ibuf += 2;
+		}
+	    }
+	    else /* to the right */
+	    {
+		register PAN_FLOAT volume, cl, crl, crr;
+
+		volume = ONE + HALF*dir;
+		cl  = volume*(ONE-dir);
+		crl = volume*(right-HALF);
+		crr = volume*(ONEHALF-right);
+
+		for (done=0; done<len; done++)
+		{
+		    obuf[0] = clip(pan, cl * ibuf[0]);
+		    obuf[1] = clip(pan, crl * ibuf[0] + crr * ibuf[1]);
+		    obuf += 2;
+		    ibuf += 2;
+		}
+	    }
+	    break;
+	case 4:
+	    if (dir <= ZERO) /* to the left */
+	    {
+		register PAN_FLOAT volume, cll, clr, cr;
+
+		volume = ONE - HALF*dir;
+		cll = volume*(ONEHALF-left);
+		clr = volume*(left-HALF);
+		cr  = volume*(ONE+dir);
+
+		for (done=0; done<len; done++)
+		{
+		    register PAN_FLOAT ibuf0, ibuf1;
+
+		    /* build stereo signal */
+		    ibuf0 = HALF*ibuf[0] + HALF*ibuf[2];
+		    ibuf1 = HALF*ibuf[1] + HALF*ibuf[3];
+
+		    /* pan it */
+		    obuf[0] = clip(pan, cll * ibuf0 + clr * ibuf1);
+		    obuf[1] = clip(pan, cr * ibuf1);
+		    obuf += 2;
+		    ibuf += 4;
+		}
+	    }
+	    else /* to the right */
+	    {
+		register PAN_FLOAT volume, cl, crl, crr;
+
+		volume = ONE + HALF*dir;
+		cl  = volume*(ONE-dir);
+		crl = volume*(right-HALF);
+		crr = volume*(ONEHALF-right);
+
+		for (done=0; done<len; done++)
+		{
+		    register PAN_FLOAT ibuf0, ibuf1;
+
+		    ibuf0 = HALF*ibuf[0] + HALF*ibuf[2];
+		    ibuf1 = HALF*ibuf[1] + HALF*ibuf[3];
+
+		    obuf[0] = clip(pan, cl * ibuf0);
+		    obuf[1] = clip(pan, crl * ibuf0 + crr * ibuf1);
+		    obuf += 2;
+		    ibuf += 4;
+		}
+	    }
+	    break;
+	default:
+	    UNEXPECTED_CHANNELS;
+	    break;
+	} /* end second switch in channel */
+	break;
+    case 4:
+	switch (ich) {
+	case 1: /* linear */
+	    {
+		register PAN_FLOAT cr, cl;
+
+		cl = HALF*left;
+		cr = HALF*right;
+
+		for (done=0; done<len; done++)
+		{
+		    obuf[0] = clip(pan, cl * ibuf[0]);
+		    obuf[2] = obuf[0];
+		    obuf[1] = clip(pan, cr * ibuf[0]);
+		    ibuf[3] = obuf[1];
+		    obuf += 4;
+		    ibuf++;
+		}
+	    }
+	    break;
+	case 2: /* simple linear panorama */
+	    if (dir <= ZERO) /* to the left */
+	    {
+		register PAN_FLOAT volume, cll, clr, cr;
+
+		volume = HALF - QUARTER*dir;
+		cll = volume * (ONEHALF-left);
+		clr = volume * (left-HALF);
+		cr  = volume * (ONE+dir);
+
+		for (done=0; done<len; done++)
+		{
+		    obuf[0] = clip(pan, cll * ibuf[0] + clr * ibuf[1]);
+		    obuf[2] = obuf[0];
+		    obuf[1] = clip(pan, cr * ibuf[1]);
+		    ibuf[3] = obuf[1];
+		    obuf += 4;
+		    ibuf += 2;
+		}
+	    }
+	    else /* to the right */
+	    {
+		register PAN_FLOAT volume, cl, crl, crr;
+
+		volume = HALF + QUARTER*dir;
+		cl  = volume * (ONE-dir);
+		crl = volume * (right-HALF);
+		crr = volume * (ONEHALF-right);
+
+		for (done=0; done<len; done++)
+		{
+		    obuf[0] = clip(pan, cl * ibuf[0]);
+		    obuf[2] = obuf[0];
+		    obuf[1] = clip(pan, crl * ibuf[0] + crr * ibuf[1]);
+		    ibuf[3] = obuf[1];
+		    obuf += 4;
+		    ibuf += 2;
+		}
+	    }
+	    break;
+	case 4:
+	    /* maybe I could improve the formula to reverse...
+	       also, turn only by quarters.
+	     */
+	    if (dir <= ZERO) /* to the left */
+	    {
+		register PAN_FLOAT cown, cright;
+
+		cright = -dir;
+		cown = ONE + dir;
+
+		for (done=0; done<len; done++)
+		{
+		    obuf[0] = clip(pan, cown*ibuf[0] + cright*ibuf[1]);
+		    obuf[1] = clip(pan, cown*ibuf[1] + cright*ibuf[3]);
+		    obuf[2] = clip(pan, cown*ibuf[2] + cright*ibuf[0]);
+		    obuf[3] = clip(pan, cown*ibuf[3] + cright*ibuf[2]);
+		    obuf += 4;
+		    ibuf += 4;		    
+		}
+	    }
+	    else /* to the right */
+	    {
+		register PAN_FLOAT cleft, cown;
+
+		cleft = dir;
+		cown = ONE - dir;
+
+		for (done=0; done<len; done++)
+		{
+		    obuf[0] = clip(pan, cleft*ibuf[2] + cown*ibuf[0]);
+		    obuf[1] = clip(pan, cleft*ibuf[0] + cown*ibuf[1]);
+		    obuf[2] = clip(pan, cleft*ibuf[3] + cown*ibuf[2]);
+		    obuf[3] = clip(pan, cleft*ibuf[1] + cown*ibuf[3]);
+		    obuf += 4;
+		    ibuf += 4;
+		}
+	    }
+	    break;
+	default:
+	    UNEXPECTED_CHANNELS;
+	    break;
+	} /* end third switch in channel */
+	break;
+    default:
+	UNEXPECTED_CHANNELS;
+	break;
+    } /* end switch out channel */
+
+    return ST_SUCCESS;
+}
+
+/*
+ * Do anything required when you stop reading samples.  
+ * Don't close input file! 
+ *
+ * Should have statistics on right, left, and output amplitudes.
+ */
+int st_pan_stop(effp)
+eff_t effp;
+{
+    pan_t pan = (pan_t) effp->priv;
+    if (pan->clipped) {
+	warn("PAN clipped %d values, maybe adjust volume?",
+	     pan->clipped);
+    }
+    return ST_SUCCESS;
+}
--- a/src/smp.c
+++ b/src/smp.c
@@ -85,9 +85,9 @@
 		ft->loops[i].length = 
 			trailer->loops[i].end - trailer->loops[i].start;
 		st_readb(ft, &(trailer->loops[i].type));
-		ft->loops[i].type = trailer->loops[8].type;
+		ft->loops[i].type = trailer->loops[i].type;
 		st_readw(ft, &(trailer->loops[i].count));
-		ft->loops[8].count = trailer->loops[8].count;
+		ft->loops[i].count = trailer->loops[i].count;
 	}
 	for(i = 0; i < 8; i++) {	/* read the 8 markers */
 	        if (fread(trailer->markers[i].name, 1, 10, ft->fp) != 10)
--- /dev/null
+++ b/src/vol.c
@@ -1,0 +1,172 @@
+/*
+ * (c) 20/03/2000 Fabien COELHO <fabien@coelho.net> for sox.
+ *
+ * Change volume of sound file, with basic linear amplitude formula.
+ * Pretty redundant with -v general option.
+ * 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 VOL_FLOAT
+#define VOL_FLOAT float
+#define VOL_FLOAT_SCAN "%f"
+#endif
+
+/* constants
+ */
+#define ZERO	  ((VOL_FLOAT)(0.0e0))
+#define LOG_10_20 ((VOL_FLOAT)(0.1151292546497022842009e0))
+#define ONE	  ((VOL_FLOAT)(1.0e0))
+#define TWENTY	  ((VOL_FLOAT)(20.0e0))
+
+#define VOL_USAGE \
+    "Usage: vol gain type" \
+    " (default type=amplitude: 1.0 is constant, <0.0 change phase;" \
+    " type=power 1.0 is constant; type=dB: 0.0 is constant, +6 doubles ampl.)"
+
+typedef struct {
+    VOL_FLOAT gain; /* amplitude gain. */
+    int clipped;    /* number of clipped values to report. */
+} * vol_t;
+
+/*
+ * Process options: gain (float) type (amplitude, power, dB)
+ */
+int st_vol_getopts(effp, n, argv) 
+eff_t effp;
+int n;
+char **argv;
+{
+    vol_t vol = (vol_t) effp->priv; 
+    
+    vol->gain = ONE; /* default is no change */
+    
+    if (n && (!sscanf(argv[0], VOL_FLOAT_SCAN, &vol->gain)))
+    {
+	fail(VOL_USAGE);
+	return ST_EOF;
+    }
+
+    /* adjust gain depending on type (what a great parser;-) */
+    if (n>1) 
+    {
+	switch (argv[1][0]) 
+	{
+	case 'd': /* decibels to amplitude */
+	case 'D':
+	    vol->gain = exp(vol->gain*LOG_10_20);
+	    break;
+	case 'p':
+	case 'P': /* power to amplitude, keep phase change */
+	    if (vol->gain > ZERO)
+		vol->gain = sqrt(vol->gain);
+	    else
+		vol->gain = -sqrt(-vol->gain);
+	    break;
+	case 'a': /* amplitude */
+	case 'A':
+	default:
+	    break;
+	}
+    }
+
+    return ST_SUCCESS;
+}
+
+/*
+ * Start processing
+ */
+int st_vol_start(effp)
+eff_t effp;
+{
+    vol_t vol = (vol_t) effp->priv;
+    
+    if (effp->outinfo.channels != effp->ininfo.channels)
+    {
+	warn("VOL 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)
+    {
+	fail("VOL cannot handle different rates (in=%ld, out=%ld)"
+	     " use resample or rate", effp->ininfo.rate, effp->outinfo.rate);
+	return ST_EOF;
+    }
+
+    vol->clipped = 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(vol_t vol, const VOL_FLOAT v)
+{
+    if (v > LONG_MAX)
+    {
+	 vol->clipped++;
+	 return LONG_MAX;
+    }
+    else if (v < -LONG_MAX)
+    {
+	vol->clipped++;
+	return -LONG_MAX;
+    }
+    /* else */
+    return (LONG) v;
+}
+
+#ifndef MIN
+#define MIN(s1,s2) ((s1)<(s2)?(s1):(s2))
+#endif
+
+/*
+ * Process data.
+ */
+int st_vol_flow(effp, ibuf, obuf, isamp, osamp)
+eff_t effp;
+LONG *ibuf, *obuf;
+LONG *isamp, *osamp;
+{
+    vol_t vol = (vol_t) effp->priv;
+    register VOL_FLOAT gain = vol->gain;
+    register LONG len;
+    
+    len = MIN(*osamp, *isamp);
+
+    /* report back dealt with amount. */
+    *isamp = len; *osamp = len;
+    
+    /* quite basic, with clipping */
+    for (;len>0; len--)
+	*obuf++ = clip(vol, gain * *ibuf++);
+
+    return ST_SUCCESS;
+}
+
+/*
+ * Do anything required when you stop reading samples.  
+ * Don't close input file! 
+ */
+int st_vol_stop(effp)
+eff_t effp;
+{
+    vol_t vol = (vol_t) effp->priv;
+    if (vol->clipped) 
+    {
+	warn("VOL clipped %d values, amplitude gain=%f too high...", 
+	     vol->clipped, vol->gain);
+    }
+    return ST_SUCCESS;
+}