shithub: sox

Download patch

ref: c7dbcf529c434f00da0d25fc7daba11a01141acb
parent: 00071341d288c17fd063d92b29cbe45f0feff564
author: rrt <rrt>
date: Mon Nov 13 19:57:29 EST 2006

Add robs@users.sf.net's bass/treble filters patch, which also adds
visualisation of filters' frequency response via Octave, improves the
self-tests, and allows .au files' number of channels and rates to be
overridden on the command-line, as for WAV files.

--- a/Changelog
+++ b/Changelog
@@ -20,6 +20,12 @@
   o Add FLAC support (robs@users.sf.net)
   o Allow encoding quality to be specified (works for Ogg too, but not
     MP3).
+  o bass and treble altering effects. (robs@users.sf.net)
+  o Visualisation of various filters' frequency response via Octave.
+    (robs@users.sf.net)
+  o Allow the rate and number of channels of .au files to be overridden
+    by command-line arguments. (robs@users.sf.net)
+  o More and better self-tests. (robs@users.sf.net)
 
 sox-12.18.2
 -----------
--- a/README
+++ b/README
@@ -1,11 +1,12 @@
 SoX: Sound eXchange
 -------------------
 
-SoX (also known as Sound eXchange) translates sound files between different
-file formats, and optionally applies various sound effects.
+SoX (also known as Sound eXchange) translates sound files between
+different file formats, and optionally applies various sound effects.
 
-SoX is intended as the Swiss Army knife of sound processing tools.  It 
-doesn't do anything very well, but sooner or later it comes in very handy.
+SoX is intended as the Swiss Army knife of sound processing tools. It
+doesn't do anything very well, but sooner or later it comes in very
+handy.
 
 This release understands:
 
@@ -25,7 +26,6 @@
   o Macintosh HCOM files
   o Amiga MAUD files
   o MP3 files (with optional external library)
-  0 Psion Record.app files
   o IRCAM SoundFile files
   o NIST SPHERE files
   o Turtle beach SampleVision files.
@@ -33,6 +33,7 @@
   o Yamaha TX-16W sampler files.
   o Sound Blaster .VOC files
   o Ogg Vorbis files (with optional external library)
+  o FLAC files (with optional external library)
   o Dialogic/OKI ADPCM files (.VOX)
   o Microsoft .WAV files
     o PCM, u-law, A-law
@@ -39,7 +40,7 @@
     o MS ADPCM, IMA ADPCM
     o GSM
     o RIFX (big endian)
-  o Psion (palmtop) A-law WVE files
+  o Psion (palmtop) A-law WVE files and Record voice notes
   o Pseudo-file formats that allow direct playing/recording
     from some audio devices under UNIX.
   o Pseudo-nul file that reads and writes from/to nowhere
@@ -49,11 +50,13 @@
   o Channel averaging, duplication, and removal (general mixer)
   o Band-pass filters
   o Band-reject filter
+  o Bass tone control
   o Compress and Expand (compand) the dynamic range of a sample.
   o Chorus effect
   o DCShift audio.  Useful to get the best volume adjustments.
   o Deemphases filter
-  o Move sound stage of CD audio to in front of you (for headphone use)
+  o Move sound stage of CD audio to in front of you (for headphone
+    use)
   o Add an echo 
   o Add a sequence of echos
   o Fade in and out
@@ -71,23 +74,28 @@
      changes using real signal theory!
   o Apply a reverb effect
   o Reverse the sound samples (to search for Satanic messages ;-)
-  o Detect periods of silence and start and stop processing based on it
-  o Change the speed of samples being played (like speeding up the motor
-    on a tape recorder)
+  o Detect periods of silence and start and stop processing based on
+    it
+  o Change the speed of samples being played (like speeding up the
+    motor on a tape recorder)
   o Display general stats on a sound sample
-  o Stretch/shorten the duration of a sound file (without affecting pitch).
+  o Stretch/shorten the duration of a sound file (without affecting
+    pitch).
   o Swap stereo channels
   o Create sounds with a simple synthesizer.
+  o Treble tone control
   o Trim audio data from beginning and end of file.
   o Add the world-famous Fender Vibro-Champ effect
   o Adjust volume of samples
   o Noise elimination using frequency profiling
+  o Resample using libsamplerate (aka Secret Rabbit code, optional
+    external library).
 
 Installing:
 
-Unless your using a pre-compiled binary version, you will need to compile
-SoX as described in the INSTALL file.  Please read that file for further
-instructions.
+Unless your using a pre-compiled binary version, you will need to
+compile SoX as described in the INSTALL file. Please read that file
+for further instructions.
 
 SoX is distributed with two text files named sox.txt and soxexam.txt.
 These files give a background on how SoX deals with sound files and
@@ -94,20 +102,20 @@
 what command line options are available to convert a sound file to
 another file format and how to apply sound effects.
 
-SoX has an auto-detect feature that attempts to figure out
-the nature of a sound file.  It does this by looking for 'magic values'
-in the header of the audio file.  If it is unable to detect the format
-of the sound file then the user must use command line options to
-inform SoX.
+SoX has an auto-detect feature that attempts to figure out the nature
+of a sound file. It does this by looking for 'magic values' in the
+header of the audio file. If it is unable to detect the format of the
+sound file then the user must use command line options to inform SoX.
 
 I hope to inspire the creation of a common base of sound processing
-tools for computer multimedia work, similar to the PBM toolkit for 
+tools for computer multimedia work, similar to the PBM toolkit for
 image manipulation.
 
-If you have bug fixes/enhancements, please send it to me as I would like
-to coordinate the releases.  Please document your changes.  I do not 
-possess every kind of computer currently sold, and SoX is now beyond 
-the phase where I can understand and test most of your contributions.
+If you have bug fixes/enhancements, please send it to me as I would
+like to coordinate the releases. Please document your changes. I do
+not possess every kind of computer currently sold, and SoX is now
+beyond the phase where I can understand and test most of your
+contributions.
 
-The majority of SoX features and source code are contributed
-by you the user.  Thank you very much for making SoX a success!
+The majority of SoX features and source code are contributed by you
+the user. Thank you very much for making SoX a success!
--- a/sox.1
+++ b/sox.1
@@ -125,6 +125,18 @@
 Prints usage information on the specified effect.  The name
 \fBall\fR can be used to disable usage on all effects.
 .TP 10
+\fB-o\fR
+Run in a mode that can be used, in conjunction with the GNU
+Octave program, to assist with the selection and configuration
+of many of the filtering effects.  For the first given effect
+that supports the \fI-o\fR option, SoX will output Octave
+commands to plot the effect's transfer function, and then exit
+without actually processing any audio.  E.g.
+
+	sox -o input-file -e highpass 1320 > plot.m
+.br
+	octave plot.m
+.TP 10
 \fB-p\fR
 Run in preview mode and run fast.  This will somewhat speed up
 SoX when the output format has a different number of channels and
@@ -322,6 +334,15 @@
 These can be dealt with using the 
 .B .ul
 format (see below).
+.br
+   It is possible to override .au file header information
+with the
+.B -r
+and
+.B -c
+options, in which case 
+.I SoX
+will issue a warning to that effect.
 .TP 10
 .B .avr
 Audio Visual Research.
@@ -705,12 +726,56 @@
 .I center
 frequency and settling around it.
 See \fBfilter\fR for a bandpass effect with steeper shoulders.
+
+This effect supports the \fI-o\fR option (see above).
+
 .TP 10
 bandpass \fIfrequency bandwidth\fB
 Butterworth bandpass filter. Description coming soon!
+
+This effect supports the \fI-o\fR option (see above).
+
 .TP 10
 bandreject \fIfrequency bandwidth\fB
 Butterworth bandreject filter.  Description coming soon!
+
+This effect supports the \fI-o\fR option (see above).
+
+.TP 10
+bass|treble \fIgain\fR [\fIfrequency\fR] [\fIslope\fR]
+Boost or cut the bass (lower) or treble (upper) frequencies of
+the audio signal using a two-pole shelving filter with (by
+default) a response similar to that of a standard hi-fi's
+(Baxandall) tone controls.
+
+\fIgain\fR is a negative or positive number that specifies the
+dB gain at 0Hz (for \fIbass\fR), or whichever is the lower of
+~22kHz and the Nyquist frequency (for \fItreble\fR).  Its useful
+range is about -20.0 (for a large cut) to +20.0 (for a large
+boost).
+.br
+   N.B. When using a positive \fIgain\fR, in order to prevent
+clipping, it may be necessary to precede this effect with a
+suitable attenuation using the \fI-v\fR option or the \fIvol\fR
+effect.  SoX will display a warning message should clipping
+occur.
+
+If desired, the filter can be fine-tuned using the following
+optional parameters (in either order):
+
+\fIfrequency\fR sets the filter's centre frequency and so can be
+used to extend or reduce the frequency range to be boosted or
+cut. The default value is 100Hz (for \fIbass\fR) or 3kHz (for
+\fItreble\fR).
+
+\fIslope\fR is a number between 0 and 1.0 that determines how
+steep the filter's shelf transition is.  Its useful range is
+about 0.3 (for a gentle slope) to 1.0 (for a steep slope).  The
+default value is 0.5.
+
+The \fIbass\fR and \fItreble\fR effects support the \fI-o\fR
+option (see above).
+
 .TP
 chorus \fIgain-in gain-out delay decay speed depth 
 .TP 10
@@ -780,6 +845,9 @@
 audio CD format.  The frequency response of pre-emphasized
 recordings is rectified.  The filtering is defined in the
 standard document ISO 908.
+
+This effect supports the \fI-o\fR option (see above).
+
 .TP 10
 earwax
 Makes sound easier to listen to on headphones.
@@ -856,9 +924,15 @@
 in the middle of the drop.
 The slope of the filter is quite gentle.
 See \fBfilter\fR for a highpass effect with sharper cutoff.
+
+This effect supports the \fI-o\fR option (see above).
+
 .TP 10
 highpass \fIfrequency\fB
 Butterworth highpass filter.  Description coming soon!
+
+This effect supports the \fI-o\fR option (see above).
+
 .TP 10
 lowp \fIfrequency\fR
 Apply a single pole recursive low-pass filter.
@@ -867,9 +941,15 @@
 in the middle of the drop.
 The slope of the filter is quite gentle.
 See \fBfilter\fR for a lowpass effect with sharper cutoff.
+
+This effect supports the \fI-o\fR option (see above).
+
 .TP 10
 lowpass \fIfrequency\fB
 Butterworth lowpass filter.  Description coming soon!
+
+This effect supports the \fI-o\fR option (see above).
+
 .TP 10
 mask
 Add "masking noise" to signal.
@@ -1295,6 +1375,10 @@
 \fIp2\fR trapezium: ON time (0..100)
 
 \fIp3\fR trapezium: falling slope position (0..100)
+.TP 10
+treble \fIgain\fR [\fIfrequency\fR] [\fIslope\fR]
+See the description of the \fIbass\fR effect for details.
+
 .TP 10
 trim \fIstart\fR [ \fIlength\fR ]
 Trim can trim off unwanted audio data from the beginning and end of the
--- a/src/Makefile.dos
+++ b/src/Makefile.dos
@@ -22,6 +22,7 @@
 EOBJ	= avg.obj band.obj bandpass.obj breject.obj btrworth.obj chorus.obj \
 	  compand.obj copy.obj dcshift.obj deemphas.obj earwax.o \
 	  echo.obj echos.obj fade.obj FFT.obj filter.obj flanger.obj \
+	  biquad.obj tone.obj \
 	  highp.obj highpass.obj lowp.obj lowpass.obj mask.obj mcompand.obj \
 	  noiseprof.obj noisered.obj phaser.obj pitch.obj pan.obj \
 	  polyphase.obj rate.obj repeat.obj resample.obj \
--- a/src/Makefile.gcc
+++ b/src/Makefile.gcc
@@ -27,6 +27,7 @@
 
 EOBJ    = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
 	  copy.o dcshift.o deemphas.o earwax.o echo.o echos.o fade.o FFT.o \
+	  biquad.o tone.o \
 	  filter.o flanger.o highp.o highpass.o lowp.o lowpass.o \
 	  mask.o mcompand.o noiseprof.o noisered.o pan.o phaser.o pitch.o \
 	  polyphas.o rate.o repeat.o resample.o reverb.o reverse.o \
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -49,6 +49,7 @@
 
 EOBJ	= avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
 	  copy.o dcshift.o deemphas.o earwax.o echo.o echos.o \
+	  biquad.o tone.o \
 	  fade.o FFT.o filter.o flanger.o highp.o highpass.o lowp.o \
 	  lowpass.o mask.o mcompand.o noiseprof.o noisered.o pan.o \
 	  phaser.o pitch.o polyphas.o rabbit.o rate.o repeat.o resample.o \
--- a/src/au.c
+++ b/src/au.c
@@ -233,13 +233,21 @@
                 break;
         }
 
+
         /* Read the sampling rate */
         st_readdw(ft, &sample_rate);
-        ft->info.rate = sample_rate;
+        if (ft->info.rate == 0 || ft->info.rate == sample_rate)
+            ft->info.rate = sample_rate;
+        else
+            st_report("User options overriding rate read in .au header");
 
         /* Read the number of channels */
         st_readdw(ft, &channels);
-        ft->info.channels = (int) channels;
+        if (ft->info.channels == -1 || ft->info.channels == (int) channels)
+            ft->info.channels = (int) channels;
+        else
+            st_report("User options overriding channels read in .au header");
+
 
         /* Skip the info string in header; print it if verbose */
         hdr_size -= SUN_HDRSIZE; /* #bytes already read */
--- a/src/band.c
+++ b/src/band.c
@@ -106,6 +106,27 @@
         else
                 band->A = sqrt(1-band->B*band->B/(4*band->C))*(1-band->C);
         band->out1 = band->out2 = 0.0;
+        if (effp->globalinfo.octave_plot_effect)
+        {
+          printf(
+            "title('SoX effect: %s centre=%g width=%g (rate=%u)')\n"
+            "xlabel('Frequency (Hz)')\n"
+            "ylabel('Amplitude Response (dB)')\n"
+            "Fs=%u;minF=10;maxF=Fs/2;\n"
+            "axis([minF maxF -75 25])\n"
+            "sweepF=logspace(log10(minF),log10(maxF),200);\n"
+            "grid on\n"
+            "[h,w]=freqz([%f 0 0],[1 %f %f],sweepF,Fs);\n"
+            "semilogx(w,20*log10(h),'b')\n"
+            "pause\n"
+            , effp->name, band->center, band->width
+            , effp->ininfo.rate, effp->ininfo.rate
+            , band->A
+            , band->B
+            , band->C
+            );
+          exit(0);
+        }
         return (ST_SUCCESS);
 }
 
--- a/src/bandpass.c
+++ b/src/bandpass.c
@@ -76,6 +76,7 @@
 
   butterworth->b [0] = -c * d * butterworth->a [0];
   butterworth->b [1] = (c - 1.0) * butterworth->a[0];
+  st_butterworth_plot(effp);
   return (ST_SUCCESS);
 }
 
--- a/src/breject.c
+++ b/src/breject.c
@@ -76,6 +76,7 @@
 
   butterworth->b [0] = butterworth->a[1];
   butterworth->b [1] = (1.0 - c) * butterworth->a[0];
+  st_butterworth_plot(effp);
   return (ST_SUCCESS);
 }
 
--- a/src/btrworth.c
+++ b/src/btrworth.c
@@ -47,6 +47,35 @@
   return (ST_SUCCESS);
 }
 
+void st_butterworth_plot (eff_t effp)
+{
+  butterworth_t butterworth = (butterworth_t) effp->priv;
+
+  if (effp->globalinfo.octave_plot_effect)
+  {
+    printf(
+      "title('SoX effect: %s centre=%g width=%g (rate=%u)')\n"
+      "xlabel('Frequency (Hz)')\n"
+      "ylabel('Amplitude Response (dB)')\n"
+      "Fs=%u;minF=10;maxF=Fs/2;\n"
+      "axis([minF maxF -95 5])\n"
+      "sweepF=logspace(log10(minF),log10(maxF),200);\n"
+      "grid on\n"
+      "[h,w]=freqz([%f %f %f],[1 %f %f],sweepF,Fs);\n"
+      "semilogx(w,20*log10(h),'b')\n"
+      "pause\n"
+      , effp->name, butterworth->frequency, butterworth->bandwidth
+      , effp->ininfo.rate, effp->ininfo.rate
+      , butterworth->a[0]
+      , butterworth->a[1]
+      , butterworth->a[2]
+      , butterworth->b[0]
+      , butterworth->b[1]
+      );
+    exit(0);
+  }
+}
+
 int st_butterworth_flow (eff_t effp, st_sample_t *ibuf, st_sample_t *obuf, 
                          st_size_t *isamp, st_size_t *osamp)
 {
--- a/src/btrworth.h
+++ b/src/btrworth.h
@@ -32,6 +32,7 @@
  */
 
 int st_butterworth_start (eff_t effp);
+void st_butterworth_plot (eff_t effp);
 int st_butterworth_flow (eff_t effp, st_sample_t *ibuf, st_sample_t *obuf,
                          st_size_t *isamp, st_size_t *osamp);
 
--- a/src/deemphas.c
+++ b/src/deemphas.c
@@ -130,6 +130,11 @@
      return (ST_SUCCESS);
 }
 
+/* filter coefficients */
+#define a1      -0.62786881719628784282
+#define b0      0.45995451989513153057
+#define b1      -0.08782333709141937339
+
 /*
  * Prepare processing.
  * Do all initializations.
@@ -158,6 +163,25 @@
           deemph->lastin = 0;
           deemph->lastout = 0.0;
      }
+     if (effp->globalinfo.octave_plot_effect)
+     {
+       printf(
+         "title('SoX effect: %s (rate=%u)')\n"
+         "xlabel('Frequency (Hz)')\n"
+         "ylabel('Amplitude Response (dB)')\n"
+         "Fs=%u;minF=10;maxF=Fs/2;\n"
+         "axis([minF maxF -25 25])\n"
+         "sweepF=logspace(log10(minF),log10(maxF),200);\n"
+         "grid on\n"
+         "[h,w]=freqz([%f %f],[1 %f],sweepF,Fs);\n"
+         "semilogx(w,20*log10(h),'b')\n"
+         "pause\n"
+         , effp->name
+         , effp->ininfo.rate, effp->ininfo.rate
+         , b0, b1, a1
+         );
+       exit(0);
+     }
      return (ST_SUCCESS);
 }
 
@@ -165,11 +189,6 @@
  * Processed signed long samples from ibuf to obuf.
  * Return number of samples processed.
  */
-
-/* filter coefficients */
-#define a1      -0.62786881719628784282
-#define b0      0.45995451989513153057
-#define b1      -0.08782333709141937339
 
 int st_deemph_flow(eff_t effp, st_sample_t *ibuf, st_sample_t *obuf, 
                    st_size_t *isamp, st_size_t *osamp)
--- a/src/handlers.c
+++ b/src/handlers.c
@@ -84,6 +84,7 @@
 st_effect_fn_t st_effect_fns[] = {
   st_avg_effect_fn,
   st_band_effect_fn,
+  st_bass_effect_fn,
   st_bandpass_effect_fn,
   st_bandreject_effect_fn,
   st_chorus_effect_fn,
@@ -124,6 +125,7 @@
   st_stretch_effect_fn,
   st_swap_effect_fn,
   st_synth_effect_fn,
+  st_treble_effect_fn,
   st_trim_effect_fn,
   st_vibro_effect_fn,
   st_vol_effect_fn,
--- a/src/highp.c
+++ b/src/highp.c
@@ -73,6 +73,26 @@
         highp->A1 = (-1 * (1 + highp->B1)) / 2;
         highp->inm1 = 0.0;
         highp->outm1 = 0.0;
+
+        if (effp->globalinfo.octave_plot_effect)
+        {
+          printf(
+            "title('SoX effect: %s cutoff=%g (rate=%u)')\n"
+            "xlabel('Frequency (Hz)')\n"
+            "ylabel('Amplitude Response (dB)')\n"
+            "Fs=%u;minF=10;maxF=Fs/2;\n"
+            "axis([minF maxF -95 5])\n"
+            "sweepF=logspace(log10(minF),log10(maxF),200);\n"
+            "grid on\n"
+            "[h,w]=freqz([%f %f],[1 %f],sweepF,Fs);\n"
+            "semilogx(w,20*log10(h),'b')\n"
+            "pause\n"
+            , effp->name, highp->cutoff
+            , effp->ininfo.rate, effp->ininfo.rate
+            , highp->A0, highp->A1, -highp->B1
+            );
+          exit(0);
+        }
         return (ST_SUCCESS);
 }
 
--- a/src/highpass.c
+++ b/src/highpass.c
@@ -71,6 +71,7 @@
 
   butterworth->b [0] = 2 * (c * c - 1.0) * butterworth->a[0];
   butterworth->b [1] = (1.0 - sqrt(2.0) * c + c * c) * butterworth->a [0];
+  st_butterworth_plot(effp);
   return (ST_SUCCESS);
 }
 
--- a/src/lowp.c
+++ b/src/lowp.c
@@ -69,6 +69,26 @@
         lowp->B = exp((-2.0 * M_PI * (lowp->cutoff / effp->ininfo.rate)));
         lowp->A = 1 - lowp->B;
         lowp->outm1 = 0.0;
+
+        if (effp->globalinfo.octave_plot_effect)
+        {
+          printf(
+            "title('SoX effect: %s cutoff=%g (rate=%u)')\n"
+            "xlabel('Frequency (Hz)')\n"
+            "ylabel('Amplitude Response (dB)')\n"
+            "Fs=%u;minF=10;maxF=Fs/2;\n"
+            "axis([minF maxF -95 5])\n"
+            "sweepF=logspace(log10(minF),log10(maxF),200);\n"
+            "grid on\n"
+            "[h,w]=freqz([%f 0],[1 %f],sweepF,Fs);\n"
+            "semilogx(w,20*log10(h),'b')\n"
+            "pause\n"
+            , effp->name, lowp->cutoff
+            , effp->ininfo.rate, effp->ininfo.rate
+            , lowp->A, -lowp->B
+            );
+          exit(0);
+        }
         return (ST_SUCCESS);
 }
 
--- a/src/lowpass.c
+++ b/src/lowpass.c
@@ -57,6 +57,7 @@
 
   butterworth->b [0] = 2 * (1.0 - c * c) * butterworth->a[0];
   butterworth->b [1] = (1.0 - sqrt(2.0) * c + c * c) * butterworth->a [0];
+  st_butterworth_plot(effp);
   return (ST_SUCCESS);
 }
 
--- a/src/play.in
+++ b/src/play.in
@@ -93,7 +93,7 @@
 # loop over arguments
 while [ $# -ne 0 ]; do
     case "$1" in
-	avg|band|bandpass|bandreject|chorus|compand|copy|cut|deemph|earwax|echo|echos|fade|filter|flanger|highp|highpass|lowp|lowpass|map|mask|mcompand|noiseprof|noisered|pan|phaser|pick|pitch|polyphase|rate|repeat|resample|reverb|reverse|silence|speed|split|stat|stretch|swap|trim|vibro|vol)
+	avg|band|bandpass|bandreject|bass|chorus|compand|copy|cut|deemph|earwax|echo|echos|fade|filter|flanger|highp|highpass|lowp|lowpass|map|mask|mcompand|noiseprof|noisered|pan|phaser|pick|pitch|polyphase|rate|repeat|resample|reverb|reverse|silence|speed|split|stat|stretch|swap|treble|trim|vibro|vol)
 	    effects="$@"
 	    break
 	    ;;
--- a/src/sox.c
+++ b/src/sox.c
@@ -69,6 +69,7 @@
 static int clipped = 0;         /* Volume change clipping errors */
 static int writing = 1;         /* are we writing to a file? assume yes. */
 static int soxpreview = 0;      /* preview mode */
+static st_globalinfo_t globalinfo;
 
 static int user_abort = 0;
 
@@ -285,7 +286,7 @@
     return(0);
 }
 
-static char *getoptstr = "+r:v:t:c:C:phsuUAaigbwlfdxVSq";
+static char *getoptstr = "+r:v:t:c:C:phsuUAaigbwlfdxVSqo";
 
 static struct option long_options[] =
 {
@@ -318,6 +319,10 @@
                 /* no return from above */
                 break;
 
+            case 'o':
+                globalinfo.octave_plot_effect = true;
+                break;
+
             case 'p':
                 soxpreview++;
                 break;
@@ -920,6 +925,8 @@
 
     for (i = 0; i < nuser_effects; i++)
     {
+        user_efftab[i].globalinfo = globalinfo;
+
         if (user_efftab[i].h->flags & ST_EFF_CHAN)
         {
             haschan++;
--- a/src/st.h
+++ b/src/st.h
@@ -43,7 +43,6 @@
 /* Minimum and maximum values a sample can hold. */
 #define ST_SAMPLE_MAX 2147483647L
 #define ST_SAMPLE_MIN (-ST_SAMPLE_MAX - 1L)
-#define ST_SAMPLE_FLOAT_UNSCALE 2147483647.0
 #define ST_SAMPLE_FLOAT_SCALE 2147483648.0
 
 #define ST_UNSIGNED_BYTE_TO_SAMPLE(d) ((st_sample_t)((d) ^ 0x80) << 24)
@@ -52,10 +51,8 @@
 #define ST_SIGNED_WORD_TO_SAMPLE(d) ((st_sample_t)(d) << 16)
 #define ST_UNSIGNED_DWORD_TO_SAMPLE(d) ((st_sample_t)((d) ^ 0x80000000L))
 #define ST_SIGNED_DWORD_TO_SAMPLE(d) ((st_sample_t)d)
-/* FIXME: This is an approximation because it 
- * doesn't account for -1.0 mapping to -FLOAT_SCALE-1. */
-#define ST_FLOAT_DWORD_TO_SAMPLE(d) ((st_sample_t)(d*ST_SAMPLE_FLOAT_UNSCALE))
-#define ST_FLOAT_DDWORD_TO_SAMPLE(d) ((st_sample_t)(d*ST_SAMPLE_FLOAT_UNSCALE))
+#define ST_FLOAT_DWORD_TO_SAMPLE(d) (d==1? ST_SAMPLE_MAX : (st_sample_t)(d*ST_SAMPLE_FLOAT_SCALE))
+#define ST_FLOAT_DDWORD_TO_SAMPLE ST_FLOAT_DWORD_TO_SAMPLE
 #define ST_SAMPLE_TO_UNSIGNED_BYTE(d) ((uint8_t)((d) >> 24) ^ 0x80)
 #define ST_SAMPLE_TO_SIGNED_BYTE(d) ((int8_t)((d) >> 24))
 #define ST_SAMPLE_TO_UNSIGNED_WORD(d) ((uint16_t)((d) >> 16) ^ 0x8000)
@@ -90,6 +87,13 @@
       { samp = ST_SAMPLE_MIN; clips++; } \
   } while (0)
 
+/* Rvalue MACRO to round and clip a double to a st_sample_t,
+ * and increment a counter if clipping occurs.
+ */
+#define ST_ROUND_CLIP_COUNT(d, clips) \
+  (d < 0? d <= ST_SAMPLE_MIN - 0.5? ++clips, ST_SAMPLE_MIN: d - 0.5 \
+        : d >= ST_SAMPLE_MAX + 0.5? ++clips, ST_SAMPLE_MAX: d + 0.5)
+
 /* MACRO to clip a normalized floating point data between 1.0 and -1.0
  * to those limits and increment a counter when clipping occurs.
  */
@@ -108,6 +112,13 @@
 #define ST_SSIZE_MAX 0x7fffffffL
 #define ST_SSIZE_MIN (-ST_SSIZE_MAX - 1L)
 
+/* Global parameters */
+
+typedef struct  st_globalinfo
+{
+    bool octave_plot_effect;/* to help user choose effect & options */
+} st_globalinfo_t;
+
 /* Signal parameters */
 
 typedef struct  st_signalinfo
@@ -285,6 +296,7 @@
 struct st_effect
 {
     char            *name;          /* effect name */
+    struct st_globalinfo globalinfo;/* global ST parameters */
     struct st_signalinfo ininfo;    /* input signal specifications */
     struct st_signalinfo outinfo;   /* output signal specifications */
     const st_effect_t *h;           /* effects driver */
--- a/src/st_i.h
+++ b/src/st_i.h
@@ -234,6 +234,7 @@
 extern const st_effect_t *st_avg_effect_fn(void);
 extern const st_effect_t *st_pick_effect_fn(void);
 extern const st_effect_t *st_band_effect_fn(void);
+extern const st_effect_t *st_bass_effect_fn(void);
 extern const st_effect_t *st_bandpass_effect_fn(void);
 extern const st_effect_t *st_bandreject_effect_fn(void);
 extern const st_effect_t *st_chorus_effect_fn(void);
@@ -273,6 +274,7 @@
 extern const st_effect_t *st_stretch_effect_fn(void);
 extern const st_effect_t *st_swap_effect_fn(void);
 extern const st_effect_t *st_synth_effect_fn(void);
+extern const st_effect_t *st_treble_effect_fn(void);
 extern const st_effect_t *st_trim_effect_fn(void);
 extern const st_effect_t *st_vibro_effect_fn(void);
 extern const st_effect_t *st_vol_effect_fn(void);
--- a/src/tests.sh
+++ b/src/tests.sh
@@ -1,219 +1,57 @@
 #!/bin/sh
 #
-# SOX Test script.
+# SOX Regression Test script.
 #
-# This script is just a quick sanity check of SOX on lossless conversions.
+# This script is just a quick sanity check of SOX on lossless format conversions.
 
 # verbose options
-#noise=-V
+#verbose=-V
 
-./sox $noise monkey.au raw1.ub
+getFormat () {
+  formatText=$1; formatFlags=""
+  case $1 in
+    al ) formatText="alaw byte" ;;
+    sb ) formatText="signed byte" ;;
+    sl ) formatText="signed long" ;;
+    sw ) formatText="signed word" ;;
+    ul ) formatText="ulaw byte" ;;
+    ub ) formatText="unsigned byte" ;;
+    uw ) formatText="unsigned word" ;;
+    raw) formatText="float"; formatFlags="-f -l" ;;
+    au ) formatFlags="-s -b" ;;
+    wav) formatFlags="-u -b" ;;
+  esac
+}
+  
+convertToAndFrom () {
+  while [ $# != 0 ]; do
+    getFormat $format1; format1Text=$formatText; format1Flags=$formatFlags
+    getFormat       $1; format2Text=$formatText; format2Flags=$formatFlags
+    ./sox $verbose -r $rate monkey.au $format1Flags input.$format1
+    ./sox $verbose -r $rate -c 1 $format1Flags input.$format1 $format2Flags intermediate.$1
+    ./sox $verbose -r $rate -c 1 $format2Flags intermediate.$1 $format1Flags output.$format1
+    if cmp -s input.$format1 output.$format1
+    then
+      echo "ok     convert \"$format1Text\" <--> \"$format2Text\"."
+    else
+      echo "*FAIL* convert \"$format1Text\" <--> \"$format2Text\"."
+      exit 1    # This allows failure inspection.
+    fi
+    rm -f input.$format1 intermediate.$1 output.$format1
+    shift
+  done
+}
 
-# Convert between unsigned bytes and signed bytes
-./sox $noise -r 8012 -c 1 raw1.ub raw1.sb
-./sox $noise -r 8012 -c 1 raw1.sb raw2.ub
-if cmp -s raw1.ub raw2.ub
-then
-    echo "Conversion between unsigned bytes and signed bytes was successful"
-else
-    echo "Error converting between signed and unsigned bytes"
-fi
-rm -f raw1.sb raw2.ub
+format1=ub
+rate=8012
+convertToAndFrom sb sw sl al uw raw
 
-./sox $noise -r 8012 -c 1 raw1.ub raw1.sw
-./sox $noise -r 8012 -c 1 raw1.sw raw2.ub
-if cmp -s raw1.ub raw2.ub
-then
-    echo "Conversion between unsigned bytes and signed words was successful"
-else
-    echo "Error converting between signed words and unsigned bytes"
-fi
-rm -f raw1.sw raw2.ub
+format1=sw
+convertToAndFrom sl ul uw raw
 
-./sox $noise -r 8012 -c 1 raw1.ub raw1.al
-./sox $noise -r 8012 -c 1 raw1.al raw2.ub
-if cmp -s raw1.ub raw2.ub
-then
-    echo "Conversion between unsigned bytes and alaw bytes was successful"
-else
-    echo "Error converting between alaw and unsigned bytes"
-fi
-rm -f raw1.al raw2.ub
-
-
-./sox $noise -r 8012 -c 1 raw1.ub raw1.uw
-./sox $noise -r 8012 -c 1 raw1.uw raw2.ub
-if cmp -s raw1.ub raw2.ub
-then
-    echo "Conversion between unsigned bytes and unsigned words was successful"
-else
-    echo "Error converting between unsigned words and unsigned bytes"
-fi
-rm -f raw1.uw raw2.ub
-
-./sox $noise -r 8012 -c 1 raw1.ub raw1.sl
-./sox $noise -r 8012 -c 1 raw1.sl raw2.ub
-if cmp -s raw1.ub raw2.ub
-then
-    echo "Conversion between unsigned bytes and signed long was successful"
-else
-    echo "Error converting between signed long and unsigned bytes"
-fi
-rm -f raw1.sl raw2.ub
-
-./sox $noise -r 8012 -c 1 raw1.ub -f -l raw1.raw
-./sox $noise -r 8012 -c 1 -f -l raw1.raw raw2.ub
-if cmp -s raw1.ub raw2.ub
-then
-    echo "Conversion between unsigned bytes and float was successful"
-else
-    echo "Error converting between float and unsigned bytes"
-fi
-rm -f raw1.raw raw2.ub
-
-rm -f raw1.ub
-./sox $noise monkey.au raw1.sw
-
-./sox $noise -r 8012 -c 1 raw1.sw raw1.ul
-./sox $noise -r 8012 -c 1 raw1.ul raw2.sw
-if cmp -s raw1.sw raw2.sw
-then
-    echo "Conversion between signed words and ulaw bytes was successful"
-else
-    echo "Error converting between ulaw and signed words"
-fi
-rm -f raw1.ul raw2.sw
-
-rm -f raw1.sw
-
-./sox $noise monkey.au -u -b monkey1.wav
-
-echo ""
-
-ext=8svx
-./sox $noise monkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-ext=aiff
-./sox $noise monkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-# AU doesn't support unsigned so use signed
-ext=au
-./sox $noise monkey1.wav -s -b convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-ext=avr
-./sox $noise monkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-ext=dat
-./sox $noise monkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-ext=hcom
-# HCOM has to be at specific sample rate.
-./sox $noise -r 5512 monkey1.wav nmonkey1.wav
-./sox $noise nmonkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s nmonkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext nmonkey1.wav monkey2.wav
-
-ext=maud
-./sox $noise monkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-ext=sf
-./sox $noise monkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-ext=smp
-./sox $noise monkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s monkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext monkey2.wav
-
-ext=voc
-./sox $noise -r 8000 monkey1.wav nmonkey1.wav
-./sox $noise nmonkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s nmonkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext nmonkey1.wav monkey2.wav
-
-ext=wve
-./sox $noise -r 8000 monkey1.wav nmonkey1.wav
-./sox $noise nmonkey1.wav convert.$ext
-./sox $noise convert.$ext -u -b monkey2.wav
-if cmp -s nmonkey1.wav monkey2.wav
-then
-    echo "Conversion between wav and $ext was successful"
-else
-    echo "Error converting between wav and $ext."
-fi
-rm -f convert.$ext nmonkey1.wav monkey2.wav
-
-exit
+format1=wav
+convertToAndFrom 8svx aiff au avr dat maud sf smp
+rate=5512
+convertToAndFrom hcom
+rate=8000
+convertToAndFrom voc wve
--- a/watcom/makefile
+++ b/watcom/makefile
@@ -23,6 +23,7 @@
 
 EOBJ = avg.obj band.obj bandpass.obj breject.obj btrworth.obj chorus.obj &
 compand.obj copy.obj dcshift.obj deemphas.obj earwax.obj echo.obj echos.obj &
+biquad.obj tone.obj &
 fade.obj FFT.obj filter.obj flanger.obj highp.obj highpass.obj lowp.obj &
 lowpass.obj mask.obj mcompand.obj noiseprof.obj noisered.obj phaser.obj &
 pitch.obj pan.obj polyphas.obj rate.obj repeat.obj resample.obj reverb.obj &
--- a/win32/stlib.dsp
+++ b/win32/stlib.dsp
@@ -197,6 +197,14 @@
 # End Source File
 # Begin Source File
 
+SOURCE=..\src\biquad.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\tone.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\g711.c
 # End Source File
 # Begin Source File