shithub: sox

Download patch

ref: 1f70a30a0d51cfd6499c4d76be8a593a91fd5a3d
parent: bd403cd4de5fd397e4d103f0e1b42d54c408bcb8
author: robs <robs>
date: Wed Jan 21 17:11:51 EST 2009

--guard, & associated changes

--- a/ChangeLog
+++ b/ChangeLog
@@ -25,6 +25,7 @@
   Deprec-  Feature    [O(ption)]                           Removal
   ated in  [F(ormat)] [E(ffect)]   Replacement             due after
   -------  ----------------------  ----------------------  -------
+  14.3.0   E norm -b, norm -i      gain -B, gain -en       14.3.0 + 1 year
   14.3.0   F flac: libFLAC 1.1.2,3 libFLAC > 1.1.3         14.3.0 + 6 months
 
 Previously deprecated features (to be removed in future):
@@ -59,11 +60,18 @@
   o Fix [2404566] segfault when converting from MS ADPCM wav file.  (robs)
   o Fix slight FLAC seek inaccuracy e.g. when using `trim' effect.  (robs)
 
+Audio device drivers:
+
+  o New native OpenBSD audio handler for play/recording.  (Alexandre Ratchov)
+  o 24-bit support for ALSA handler.  (robs)
+  o Warn if ALSA under/overrun.  (robs)
+
 Effects:
 
   o New `overdrive' effect.  (robs)
   o New `pluck' and `tpdf' types for `synth'.  (robs)
   o Can now set common parameters for multiple `synth' channels.  (robs)
+  o Richer gain/normalise options.  (robs)
   o Fix [2487589] `dither' clipping detection & handling.  (robs)
   o Fix `repeat' sometimes stopping repeating too soon.  (robs)
   o Fix `repeat' sometimes repeating wrong audio segments.  (robs)
@@ -75,14 +83,13 @@
 
 Other new features:
 
-  o New native OpenBSD audio handler for play/recording.  (Alexandre Ratchov)
-  o 24-bit support for ALSA handler.  (robs)
-  o Warn if ALSA under/overrun.  (robs)
+  o Added ability to create shared DLL's on cygwin (cbagwell)
+  o New `--guard' & `--norm' options; use temporary files to guard against
+    clipping for many, but not currently all, effects.  (robs)
   o New `--ignore-length' option to ignore length in input file
     header (for simple encodings); instead, read to end of file.  (robs)
-  o Added ability to create shared DLL's on cygwin (cbagwell)
-  o Grouped files, e.g. play -r 6k "*.vox" plays all at 6k.  (robs)
   o New `--temp DIRECTORY' option.  (robs)
+  o Grouped files, e.g. play -r 6k "*.vox" plays all at 6k.  (robs)
   o New bitrate, time in seconds, & total options for soxi.  (robs)
 
 Other bug fixes:
--- a/src/.cvsignore
+++ b/src/.cvsignore
@@ -1,5 +1,4 @@
 Makefile Makefile.in
-sox_sample_test
 example0 example1 example2
 soxconfig.h.in soxconfig.h soxstdint.h
 .deps
--- a/src/8svx.c
+++ b/src/8svx.c
@@ -254,7 +254,6 @@
 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
 {
         priv_t * p = (priv_t * ) ft->priv;
-        SOX_SAMPLE_LOCALS;
 
         unsigned char datum;
         size_t done = 0, i;
@@ -263,7 +262,7 @@
 
         while(done < len) {
                 for (i = 0; i < ft->signal.channels; i++) {
-                        datum = SOX_SAMPLE_TO_SIGNED_8BIT(*buf++, ft->clips);
+                        datum = SOX_SAMPLE_TO_SIGNED_8BIT(*buf++);
                         /* FIXME: Needs to pass ft struct and not FILE */
                         putc(datum, p->ch[i]);
                 }
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -28,16 +28,16 @@
 
 # Format with: !xargs echo|tr ' ' '\n'|sort|column|expand|sed 's/^/  /'
 set(effects_srcs
-  bend            dither          lowfir          phaser          stat
-  biquad          earwax          mcompand        rate            stretch
-  biquads         echo            mixer           remix           swap
-  chorus          echos           noiseprof       repeat          synth
-  compand         fade            noisered        reverb          tempo
-  compandt        fft4g           normalise       reverse         tremolo
+  bend            dither          loudness        phaser          stat
+  biquad          earwax          lowfir          rate            stretch
+  biquads         echo            mcompand        remix           swap
+  chorus          echos           mixer           repeat          synth
+  compand         fade            noiseprof       reverb          tempo
+  compandt        fft4g           noisered        reverse         tremolo
   contrast        filter          output          silence         trim
   dcshift         flanger         overdrive       skeleff         vol
-  delay           input           pad             speed
-  dft_filter      loudness        pan             splice
+  delay           gain            pad             speed
+  dft_filter      input           pan             splice
 )
 set(formats_srcs
   8svx            dat             ima-fmt         s3-fmt          u3-fmt
@@ -62,7 +62,6 @@
 )
 add_executable(${PROJECT_NAME} ${PROJECT_NAME}.c)
 target_link_libraries(${PROJECT_NAME} lib${PROJECT_NAME} lpc10 ${optional_libs})
-add_executable(sox_sample_test sox_sample_test.c)
 add_executable(example0 example0.c)
 target_link_libraries(example0 lib${PROJECT_NAME} lpc10 ${optional_libs})
 add_executable(example1 example1.c)
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -28,7 +28,7 @@
 #########################
 
 bin_PROGRAMS = sox
-EXTRA_PROGRAMS = example0 example1 example2 sox_sample_test
+EXTRA_PROGRAMS = example0 example1 example2
 lib_LTLIBRARIES = libsox.la
 include_HEADERS = sox.h
 nodist_include_HEADERS = soxstdint.h
@@ -37,7 +37,6 @@
 example0_SOURCES = example0.c
 example1_SOURCES = example1.c
 example2_SOURCES = example2.c
-sox_sample_test_SOURCES = sox_sample_test.c sox_sample_test.h
 
 ###############################################
 # libsox - Dynamic Libraries for File Formats #
@@ -274,7 +273,7 @@
 	dither_fir.h dither_iir.h earwax.c echo.c echos.c effects.c effects.h \
 	effects_i.c fade.c fft4g.c fft4g.h fifo.h filter.c flanger.c input.c \
 	ladspa.c loudness.c mcompand.c mcompand_xover.h mixer.c noiseprof.c \
-	noisered.c noisered.h normalise.c output.c pad.c pan.c phaser.c \
+	noisered.c noisered.h gain.c output.c pad.c pan.c phaser.c \
 	rate.c rate_filters.h rate_half_fir.h \
 	rate_poly_fir0.h rate_poly_fir.h remix.c repeat.c reverb.c \
 	reverse.c silence.c skeleff.c speed.c splice.c stat.c swap.c stretch.c \
@@ -400,7 +399,7 @@
 	     CMakeLists.txt soxstdint.h.cmake soxconfig.h.cmake \
 	     tests.sh testall.sh tests.bat testall.bat test-comments
 
-all: sox$(EXEEXT) play rec soxi sox_sample_test$(EXEEXT) example0$(EXEEXT) example1$(EXEEXT) example2$(EXEEXT)
+all: sox$(EXEEXT) play rec soxi example0$(EXEEXT) example1$(EXEEXT) example2$(EXEEXT)
 
 play rec: sox$(EXEEXT)
 	if test "$(PLAYRECLINKS)" = "yes"; then	\
@@ -430,7 +429,6 @@
 
 clean-local:
 	$(RM) play rec soxi
-	$(RM) sox_sample_test$(EXEEXT)
 	$(RM) example0$(EXEEXT) example1$(EXEEXT) example2$(EXEEXT)
 
 distclean-local:
--- a/src/adpcms.c
+++ b/src/adpcms.c
@@ -251,8 +251,7 @@
   short word;
 
   while (count < length) {
-    SOX_SAMPLE_LOCALS;
-    word = SOX_SAMPLE_TO_SIGNED_16BIT(*buffer++, ft->clips);
+    word = SOX_SAMPLE_TO_SIGNED_16BIT(*buffer++);
 
     byte <<= 4;
     byte |= lsx_adpcm_encode(word, &state->encoder) & 0x0F;
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -187,7 +187,6 @@
   priv_t             * p = (priv_t *)ft->priv;
   size_t             done, i, n;
   snd_pcm_sframes_t  actual;
-  SOX_SAMPLE_LOCALS;
 
   for (done = 0; done < len; done += n) {
     i = n = min(len - done, p->buf_len);
@@ -194,38 +193,38 @@
     switch (p->format) {
       case SND_PCM_FORMAT_S8: {
         int8_t * buf1 = (int8_t *)p->buf;
-        while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_8BIT(*buf++, ft->clips);
+        while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_8BIT(*buf++);
         break;
       }
       case SND_PCM_FORMAT_U8: {
         uint8_t * buf1 = (uint8_t *)p->buf;
-        while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips);
+        while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++);
         break;
       }
       case SND_PCM_FORMAT_S16: {
         int16_t * buf1 = (int16_t *)p->buf;
         if (ft->encoding.reverse_bytes) while (i--)
-          *buf1++ = lsx_swapw(SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips));
+          *buf1++ = lsx_swapw(SOX_SAMPLE_TO_SIGNED_16BIT(*buf++));
         else
-          while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
+          while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_16BIT(*buf++);
         break;
       }
       case SND_PCM_FORMAT_U16: {
         uint16_t * buf1 = (uint16_t *)p->buf;
         if (ft->encoding.reverse_bytes) while (i--)
-          *buf1++ = lsx_swapw(SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++, ft->clips));
+          *buf1++ = lsx_swapw(SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++));
         else
-          while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++, ft->clips);
+          while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_16BIT(*buf++);
         break;
       }
       case SND_PCM_FORMAT_S24: {
         int24_t * buf1 = (int24_t *)p->buf;
-        while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_24BIT(*buf++, ft->clips);
+        while (i--) *buf1++ = SOX_SAMPLE_TO_SIGNED_24BIT(*buf++);
         break;
       }
       case SND_PCM_FORMAT_U24: {
         uint24_t * buf1 = (uint24_t *)p->buf;
-        while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_24BIT(*buf++, ft->clips);
+        while (i--) *buf1++ = SOX_SAMPLE_TO_UNSIGNED_24BIT(*buf++);
         break;
       }
     }
--- a/src/amr.h
+++ b/src/amr.h
@@ -117,8 +117,7 @@
   size_t done;
 
   for (done = 0; done < len; ++done) {
-    SOX_SAMPLE_LOCALS;
-    p->pcm[p->pcm_index++] = SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
+    p->pcm[p->pcm_index++] = SOX_SAMPLE_TO_SIGNED_16BIT(*buf++);
     if (p->pcm_index == AMR_FRAME) {
       p->pcm_index = 0;
       if (!encode_1_frame(ft))
--- a/src/ao.c
+++ b/src/ao.c
@@ -74,12 +74,11 @@
   return SOX_SUCCESS;
 }
 
-static void sox_sw_write_buf(char *buf1, sox_sample_t const * buf2, size_t len, sox_bool swap, size_t * clips)
+static void sox_sw_write_buf(char *buf1, sox_sample_t const * buf2, size_t len, sox_bool swap)
 {
     while (len--)
     {
-        SOX_SAMPLE_LOCALS;
-        uint16_t datum = SOX_SAMPLE_TO_SIGNED_16BIT(*buf2++, *clips);
+        uint16_t datum = SOX_SAMPLE_TO_SIGNED_16BIT(*buf2++);
         if (swap)
             datum = lsx_swapw(datum);
         *(uint16_t *)buf1 = datum;
@@ -97,8 +96,7 @@
 
   aobuf_size = (ft->encoding.bits_per_sample >> 3) * len;
 
-  sox_sw_write_buf(ao->buf, buf, len, ft->encoding.reverse_bytes,
-                   &(ft->clips));
+  sox_sw_write_buf(ao->buf, buf, len, ft->encoding.reverse_bytes);
   if (ao_play(ao->device, (void *)ao->buf, aobuf_size) == 0)
     return 0;
 
--- a/src/band.h
+++ b/src/band.h
@@ -40,7 +40,8 @@
 
   p->a2 = exp(-2 * M_PI * bw_Hz / effp->in_signal.rate);
   p->a1 = -4 * p->a2 / (1 + p->a2) * cos(2 * M_PI * p->fc / effp->in_signal.rate);
-  if (p->filter_type == filter_BPF_SPK_N)
-    p->b0 = sqrt(((1+p->a2) * (1+p->a2) - p->a1*p->a1) * (1-p->a2) / (1+p->a2));
-  else
-    p->b0 = sqrt(1 - p->a1 * p->a1 / (4 * p->a2)) * (1 - p->a2);
+  p->b0 = sqrt(1 - p->a1 * p->a1 / (4 * p->a2)) * (1 - p->a2);
+  if (p->filter_type == filter_BPF_SPK_N) {
+    mult = sqrt(((1+p->a2) * (1+p->a2) - p->a1*p->a1) * (1-p->a2) / (1+p->a2)) / p->b0;
+    p->b0 *= mult;
+  }
--- a/src/bend.c
+++ b/src/bend.c
@@ -154,7 +154,7 @@
     ++p->in_pos;
 
     /* As long as we have not yet collected enough data just read in */
-    p->gInFIFO[p->gRover] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i], effp->clips);
+    p->gInFIFO[p->gRover] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i]);
     obuf[i] = SOX_FLOAT_32BIT_TO_SAMPLE(
         p->gOutFIFO[p->gRover - inFifoLatency], effp->clips);
     p->gRover++;
--- a/src/biquad.h
+++ b/src/biquad.h
@@ -18,6 +18,7 @@
 #ifndef biquad_included
 #define biquad_included
 
+#define LSX_EFF_ALIAS
 #include "sox_i.h"
 
 typedef enum {
@@ -73,8 +74,5 @@
 int lsx_biquad_start(sox_effect_t * effp);
 int lsx_biquad_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf,
                         size_t *isamp, size_t *osamp);
-
-#undef lsx_fail
-#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail
 
 #endif
--- a/src/biquads.c
+++ b/src/biquads.c
@@ -164,7 +164,7 @@
   priv_t * p = (priv_t *)effp->priv;
   double w0 = 2 * M_PI * p->fc / effp->in_signal.rate;
   double A  = exp(p->gain / 40 * log(10.));
-  double alpha = 0;
+  double alpha = 0, mult = dB_to_linear(max(p->gain, 0));
 
   if (w0 > M_PI) {
     lsx_fail("frequency must be less than half the sample-rate (Nyquist rate)");
@@ -362,8 +362,11 @@
       {double g = dB_to_linear(19.9 - linear_to_dB(
             (p->b0 + p->b1 + p->b2) / (p->a0 + p->a1 + p->a2)));
       p->b0 *= g; p->b1 *= g; p->b2 *= g;}
+      mult = dB_to_linear(19.9);
       break;
   }
+  if (effp->in_signal.mult)
+    *effp->in_signal.mult /= mult;
   return lsx_biquad_start(effp);
 }
 
--- a/src/chorus.c
+++ b/src/chorus.c
@@ -333,7 +333,7 @@
 static sox_effect_handler_t sox_chorus_effect = {
   "chorus",
   "gain-in gain-out delay decay speed depth [ -s | -t ]",
-  SOX_EFF_LENGTH,
+  SOX_EFF_LENGTH | SOX_EFF_GAIN,
   sox_chorus_getopts,
   sox_chorus_start,
   sox_chorus_flow,
--- a/src/compand.c
+++ b/src/compand.c
@@ -275,7 +275,7 @@
 sox_effect_handler_t const * sox_compand_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "compand", compand_usage, SOX_EFF_MCHAN,
+    "compand", compand_usage, SOX_EFF_MCHAN | SOX_EFF_GAIN,
     getopts, start, flow, drain, stop, kill, sizeof(priv_t)
   };
   return &handler;
--- a/src/coreaudio.c
+++ b/src/coreaudio.c
@@ -285,7 +285,7 @@
   p = &ac->buffer[ac->buf_offset];
 
   while (samp_left--)
-    *p++ = SOX_SAMPLE_TO_FLOAT_32BIT(*buf++, ft->clips);
+    *p++ = SOX_SAMPLE_TO_FLOAT_32BIT(*buf++);
 
   ac->buf_offset += len;
 
--- a/src/dat.c
+++ b/src/dat.c
@@ -104,6 +104,7 @@
       /* Read a complete set of channels */
       sscanf(inpstr," %*s%n", &inpPtr);
       for (i=0; i<ft->signal.channels; i++) {
+        SOX_SAMPLE_LOCALS;
         retc = sscanf(&inpstr[inpPtr]," %lg%n", &sampval, &inpPtrInc);
         inpPtr += inpPtrInc;
         if (retc != 1) {
@@ -110,8 +111,7 @@
           lsx_fail_errno(ft,SOX_EOF,"Unable to read sample.");
           return 0;
         }
-        sampval *= SOX_SAMPLE_MAX;
-        *buf++ = SOX_ROUND_CLIP_COUNT(sampval, ft->clips);
+        *buf++ = SOX_FLOAT_64BIT_TO_SAMPLE(sampval, ft->clips);
         done++;
       }
     }
@@ -135,8 +135,7 @@
       sprintf(s," %15.8g ",dat->timevalue);
       lsx_writes(ft, s);
       for (i=0; i<ft->signal.channels; i++) {
-        SOX_SAMPLE_LOCALS;
-        sampval = SOX_SAMPLE_TO_FLOAT_64BIT(*buf++, ft->clips);
+        sampval = SOX_SAMPLE_TO_FLOAT_64BIT(*buf++);
         sprintf(s," %15.8g", sampval);
         lsx_writes(ft, s);
         done++;
--- a/src/dcshift.c
+++ b/src/dcshift.c
@@ -173,7 +173,7 @@
    "shift [ limitergain ]\n"
    "       The peak limiter has a gain much less than 1.0 (ie 0.05 or 0.02) which is only\n"
    "       used on peaks to prevent clipping. (default is no limiter)",
-   SOX_EFF_MCHAN,
+   SOX_EFF_MCHAN | SOX_EFF_GAIN,
    sox_dcshift_getopts,
    sox_dcshift_start,
    sox_dcshift_flow,
--- a/src/dft_filter.c
+++ b/src/dft_filter.c
@@ -79,7 +79,7 @@
     p->samples_in += (int)*isamp;
 
     for (i = *isamp; i; --i)
-      *t++ = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf++, effp->clips);
+      *t++ = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf++);
     filter(p);
   }
   else *isamp = 0;
@@ -122,7 +122,7 @@
 sox_effect_handler_t const * sox_dft_filter_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    NULL, NULL, 0, NULL, start, flow, drain, stop, NULL, 0
+    NULL, NULL, SOX_EFF_GAIN, NULL, start, flow, drain, stop, NULL, 0
   };
   return &handler;
 }
--- a/src/dither.c
+++ b/src/dither.c
@@ -24,10 +24,6 @@
 #include <assert.h>
 
 #define PREC effp->out_signal.precision
-#define SOX_ROUND_PREC_CLIP_COUNT(d, clips) \
-  ((d) < 0? (d) <= SOX_SAMPLE_MIN - 0.5? ++(clips), SOX_SAMPLE_MIN: (d) - 0.5 \
-  : (d) >= SOX_SAMPLE_MAX - (1 << (31-PREC)) + 0.5? \
-  ++(clips), SOX_SAMPLE_MAX - (1 << (31-PREC)): (d) + 0.5)
 
 typedef enum {Pdf_rectangular, Pdf_triangular, Pdf_gaussian} pdf_type_t;
 static lsx_enum_item const pdf_types[] = {
@@ -134,12 +130,12 @@
   {48000, iir,  4, 21.0, ges48, Shape_gesemann},
   {44100, iir,  4, 21.2, ges44, Shape_gesemann},
   {48000, fir, 16, 28.8, shi48, Shape_shibata},
-  {44100, fir, 20, 32.5, shi44, Shape_shibata},
+  {44100, fir, 20, 32.6, shi44, Shape_shibata},
   {37800, fir, 16, 22.7, shi38, Shape_shibata},
   {32000, fir, 16, 15.7, shi32, Shape_shibata},
   {22050, fir, 15,  9.0, shi22, Shape_shibata},
   {48000, fir, 16, 23.5, shl48, Shape_low_shibata},
-  {44100, fir, 15, 23.0, shl44, Shape_low_shibata},
+  {44100, fir, 15, 23.1, shl44, Shape_low_shibata},
   {44100, fir, 20, 37.5, shh44, Shape_high_shibata},
   {    0, fir,  0,  0.0,  NULL, Shape_none},
 };
@@ -154,6 +150,7 @@
   double        previous_outputs[MAX_N * 2];
   size_t        pos;
   double const * coefs;
+  sox_sample_t  offset;
   int           (*flow)(sox_effect_t *, const sox_sample_t *, sox_sample_t *, size_t *, size_t *);
 } priv_t;
 
@@ -189,7 +186,7 @@
   size_t len = *isamp = *osamp = min(*isamp, *osamp);
 
   while (len--) {
-    double d = *ibuf++ + floor(p->am0 * RANQD1) + floor(p->am1 * RANQD1);
+    double d = *ibuf++ + p->offset + floor(p->am0 * RANQD1) + floor(p->am1 * RANQD1);
     SOX_SAMPLE_CLIP_COUNT(d, effp->clips);
     *obuf++ = d;
   }
@@ -227,36 +224,39 @@
   if (PREC > 24)
     return SOX_EFF_NULL;   /* Dithering not needed at this resolution */
 
-  if (!p->filter_name)
-    p->flow = flow_no_shape;
-  else {
+  p->flow = flow_no_shape;
+  if (p->filter_name) {
     filter_t const * f;
 
     for (f = filters; f->len && (f->name != p->filter_name || fabs(effp->in_signal.rate - f->rate) / f->rate > .05); ++f);
     if (!f->len) {
-      lsx_fail("no `%s' filter is available for rate %g", lsx_find_enum_value(p->filter_name, filter_names)->text, effp->in_signal.rate);
-      return SOX_EOF;
+      lsx_warn("no `%s' filter is available for rate %g", lsx_find_enum_value(p->filter_name, filter_names)->text, effp->in_signal.rate);
     }
-    assert(f->len <= MAX_N);
-    if (f->type == fir) switch(f->len) {
-      case  5: p->flow = flow_fir_5 ; break;
-      case  9: p->flow = flow_fir_9 ; break;
-      case 15: p->flow = flow_fir_15; break;
-      case 16: p->flow = flow_fir_16; break;
-      case 20: p->flow = flow_fir_20; break;
-      default: assert(sox_false);
-    } else switch(f->len) {
-      case  4: p->flow = flow_iir_4 ; break;
-      default: assert(sox_false);
+    else {
+      assert(f->len <= MAX_N);
+      if (f->type == fir) switch(f->len) {
+        case  5: p->flow = flow_fir_5 ; break;
+        case  9: p->flow = flow_fir_9 ; break;
+        case 15: p->flow = flow_fir_15; break;
+        case 16: p->flow = flow_fir_16; break;
+        case 20: p->flow = flow_fir_20; break;
+        default: assert(sox_false);
+      } else switch(f->len) {
+        case  4: p->flow = flow_iir_4 ; break;
+        default: assert(sox_false);
+      }
+      p->coefs = f->coefs;
+      mult = dB_to_linear(f->gain);
     }
-    p->coefs = f->coefs;
-    mult = dB_to_linear(f->gain);
   }
   p->am1 = p->depth / (1 << PREC);
   p->am0 = (p->pdf == Pdf_triangular) * p->am1;
+  if (effp->out_encoding->half_bit && PREC < 32)
+    p->offset = 1 << (31 - PREC);
 
   if (effp->flow == 0 && effp->in_signal.mult)
-    *effp->in_signal.mult *= 1 - (p->am0 + p->am1) * mult;
+    *effp->in_signal.mult *=
+      1 - (p->offset * (1./SOX_SAMPLE_MAX) + p->am0 + p->am1) * mult;
 
   lsx_debug("pdf=%s filter=%s depth=%g", lsx_find_enum_value(p->pdf, pdf_types)->text, lsx_find_enum_value(p->filter_name, filter_names)->text, p->depth);
   return SOX_SUCCESS;
--- a/src/dither_fir.h
+++ b/src/dither_fir.h
@@ -7,12 +7,12 @@
 
   while (len--) {
     double r = floor(p->am0 * RANQD1) + floor(p->am1 * RANQD1);
-    double error, d = *ibuf++;
+    double error, d = *ibuf++ + p->offset;
     int j = 0;
     CONVOLVE
     assert(j == N);
-    *obuf = SOX_ROUND_PREC_CLIP_COUNT(d + r, effp->clips);
-    error = ((*obuf++ + (1 << (31-PREC))) & (-1 << (32-PREC))) - d;
+    *obuf = SOX_ROUND_CLIP_COUNT(d + r, effp->clips);
+    error = (*obuf++ & (-1 << (32-PREC))) + (1 << (31-PREC)) - d;
     p->pos = p->pos? p->pos - 1 : p->pos - 1 + N;
     p->previous_errors[p->pos + N] = p->previous_errors[p->pos] = error;
   }
--- a/src/dither_iir.h
+++ b/src/dither_iir.h
@@ -12,9 +12,9 @@
     int j = 0;
     CONVOLVE
     assert(j == N);
-    d = *ibuf++ - output;
-    *obuf = SOX_ROUND_PREC_CLIP_COUNT(d + r, effp->clips);
-    error = ((*obuf++ + (1 << (31-PREC))) & (-1 << (32-PREC))) - d;
+    d = *ibuf++ + p->offset - output;
+    *obuf = SOX_ROUND_CLIP_COUNT(d + r, effp->clips);
+    error = (*obuf++ & (-1 << (32-PREC))) + (1 << (31-PREC)) - d;
     p->pos = p->pos? p->pos - 1 : p->pos - 1 + N;
     p->previous_errors[p->pos + N] = p->previous_errors[p->pos] = error;
     p->previous_outputs[p->pos + N] = p->previous_outputs[p->pos] = output;
--- a/src/earwax.c
+++ b/src/earwax.c
@@ -62,6 +62,8 @@
     return SOX_EOF;
   }
   memset(p->tap, 0, NUMTAPS * sizeof(*p->tap)); /* zero tap memory */
+  if (effp->in_signal.mult)
+    *effp->in_signal.mult *= dB_to_linear(-4.4);
   return SOX_SUCCESS;
 }
 
--- a/src/echo.c
+++ b/src/echo.c
@@ -248,7 +248,7 @@
 static sox_effect_handler_t sox_echo_effect = {
   "echo",
   "gain-in gain-out delay decay [ delay decay ... ]",
-  SOX_EFF_LENGTH,
+  SOX_EFF_LENGTH | SOX_EFF_GAIN,
   sox_echo_getopts,
   sox_echo_start,
   sox_echo_flow,
--- a/src/echos.c
+++ b/src/echos.c
@@ -260,7 +260,7 @@
 static sox_effect_handler_t sox_echos_effect = {
   "echos",
   "gain-in gain-out delay decay [ delay decay ... ]",
-  SOX_EFF_LENGTH,
+  SOX_EFF_LENGTH | SOX_EFF_GAIN,
   sox_echos_getopts,
   sox_echos_start,
   sox_echos_flow,
--- a/src/effects.c
+++ b/src/effects.c
@@ -15,6 +15,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#define LSX_EFF_ALIAS
 #include "sox_i.h"
 #include "getopt.h"
 #include <assert.h>
@@ -23,12 +24,6 @@
   #include <strings.h>
 #endif
 
-
-#undef lsx_fail
-#undef lsx_report
-#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail
-#define lsx_report sox_globals.subsystem=effp->handler.name,lsx_report
-
 #define DEBUG_EFFECTS_CHAIN 0
 
 /* FIXME: Not thread safe using globals */
@@ -99,7 +94,7 @@
 /* Effects chain: */
 
 sox_effects_chain_t * sox_create_effects_chain(
-    sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc)
+    sox_encodinginfo_t const * in_enc, sox_encodinginfo_t * out_enc)
 {
   sox_effects_chain_t * result = lsx_calloc(1, sizeof(sox_effects_chain_t));
   result->global_info = sox_effects_globals;
@@ -150,6 +145,8 @@
     effp->out_signal.rate = in->rate;
   if (!(effp->handler.flags & SOX_EFF_PREC))
     effp->out_signal.precision = in->precision;
+  if (!(effp->handler.flags & SOX_EFF_GAIN))
+    effp->out_signal.mult = in->mult;
 
   effp->flows =
     (effp->handler.flags & SOX_EFF_MCHAN)? 1 : effp->in_signal.channels;
@@ -167,6 +164,8 @@
     free(eff0.priv);
     return SOX_EOF;
   }
+  if (in->mult)
+    lsx_debug("mult=%g", *in->mult);
 
   *in = effp->out_signal;
 
--- a/src/effects_i.c
+++ b/src/effects_i.c
@@ -18,11 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#define LSX_EFF_ALIAS
 #include "sox_i.h"
 #include <string.h>
-
-#undef lsx_fail
-#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail
 
 int lsx_usage(sox_effect_t * effp)
 {
--- a/src/example2.c
+++ b/src/example2.c
@@ -82,15 +82,14 @@
   /* Read in and process blocks of audio for the selected period or until EOF: */
   for (blocks = 0; sox_read(in, buf, block_size) == block_size && blocks * block_period < period; ++blocks) {
     double left = 0, right = 0;
-    size_t i, clips = 0;
+    size_t i;
     static const char line[] = "===================================";
     int l, r;
 
     for (i = 0; i < block_size; ++i) {
-      SOX_SAMPLE_LOCALS;
       /* convert the sample from SoX's internal format to a `double' for
        * processing in this application: */
-      double sample = SOX_SAMPLE_TO_FLOAT_64BIT(buf[i], clips);
+      double sample = SOX_SAMPLE_TO_FLOAT_64BIT(buf[i]);
 
       /* The samples for each channel are interleaved; in this example
        * we allow only stereo audio, so the left channel audio can be found in
--- a/src/ffmpeg.c
+++ b/src/ffmpeg.c
@@ -406,9 +406,8 @@
   do {
     /* If output frame is not full, copy data into it */
     if (ffmpeg->samples_index < ffmpeg->audio_input_frame_size) {
-      SOX_SAMPLE_LOCALS;
       for (; nread < len && ffmpeg->samples_index < ffmpeg->audio_input_frame_size; nread++)
-        ffmpeg->samples[ffmpeg->samples_index++] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[nread], ft->clips);
+        ffmpeg->samples[ffmpeg->samples_index++] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[nread]);
     }
 
     /* If output frame full or no more data to read, write it out */
--- a/src/flac.c
+++ b/src/flac.c
@@ -441,22 +441,21 @@
   unsigned i;
 
   for (i = 0; i < len; ++i) {
-    SOX_SAMPLE_LOCALS;
-    long pcm = SOX_SAMPLE_TO_SIGNED_32BIT(sampleBuffer[i], ft->clips);
+    long pcm = SOX_SAMPLE_TO_SIGNED_32BIT(sampleBuffer[i]);
     p->decoded_samples[i] = pcm >> (32 - p->bits_per_sample);
     switch (p->bits_per_sample) {
       case  8: p->decoded_samples[i] =
-          SOX_SAMPLE_TO_SIGNED_8BIT(sampleBuffer[i], ft->clips);
+          SOX_SAMPLE_TO_SIGNED_8BIT(sampleBuffer[i]);
         break;
       case 16: p->decoded_samples[i] =
-          SOX_SAMPLE_TO_SIGNED_16BIT(sampleBuffer[i], ft->clips);
+          SOX_SAMPLE_TO_SIGNED_16BIT(sampleBuffer[i]);
         break;
       case 24: p->decoded_samples[i] = /* sign extension: */
-          SOX_SAMPLE_TO_SIGNED_24BIT(sampleBuffer[i],ft->clips) << 8;
+          SOX_SAMPLE_TO_SIGNED_24BIT(sampleBuffer[i]) << 8;
         p->decoded_samples[i] >>= 8;
         break;
       case 32: p->decoded_samples[i] =
-          SOX_SAMPLE_TO_SIGNED_32BIT(sampleBuffer[i],ft->clips);
+          SOX_SAMPLE_TO_SIGNED_32BIT(sampleBuffer[i]);
         break;
     }
   }
--- a/src/formats.c
+++ b/src/formats.c
@@ -428,6 +428,7 @@
         if (magic)
           filetype = magic_file(magic, path);
         if (filetype && (
+              lsx_strends(filetype, "/unknown") ||
               !strcmp(filetype, "application/octet-stream") ||
               !strncmp(filetype, "text/plain", (size_t)10) ))
           filetype = NULL;
--- a/src/formats_i.c
+++ b/src/formats_i.c
@@ -90,14 +90,6 @@
   return SOX_EOF;
 }
 
-sox_sample_t lsx_sample_max(sox_encodinginfo_t const * encoding)
-{
-  unsigned precision = encoding->encoding == SOX_ENCODING_FLOAT?
-    SOX_SAMPLE_PRECISION : sox_precision(encoding->encoding, encoding->bits_per_sample);
-  unsigned shift = SOX_SAMPLE_PRECISION - min(precision, SOX_SAMPLE_PRECISION);
-  return (SOX_SAMPLE_MAX >> shift) << shift;
-}
-
 /* Read in a buffer of data of length len bytes.
  * Returns number of bytes read.
  */
--- /dev/null
+++ b/src/gain.c
@@ -1,0 +1,248 @@
+/* libSoX effect: gain/norm/etc.   (c) 2008-9 robs@users.sourceforge.net
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#define LSX_EFF_ALIAS
+#include "sox_i.h"
+#include <ctype.h>
+#include <string.h>
+
+typedef struct {
+  sox_bool      do_equalise, do_balance, do_balance_no_clip;
+  sox_bool      do_restore, make_headroom, do_normalise, do_scan;
+  double        fixed_gain; /* Valid only in channel 0 */
+
+  double        mult;
+  double        restore;
+  double        rms;
+  off_t         num_samples;
+  sox_sample_t  min, max;
+  FILE          * tmp_file;
+} priv_t;
+
+static int create(sox_effect_t * effp, int argc, char * * argv)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  char const * q;
+  for (--argc, ++argv; argc && **argv == '-' && !isdigit(argv[0][1]) &&
+      argv[0][1] != '.'; --argc, ++argv)
+    for (q = &argv[0][1]; *q; ++q) switch (*q) {
+      case 'n': p->do_scan = p->do_normalise = sox_true; break;
+      case 'e': p->do_scan = p->do_equalise = sox_true; break;
+      case 'B': p->do_scan = p->do_balance = sox_true; break;
+      case 'b': p->do_scan = p->do_balance_no_clip = sox_true; break;
+      case 'r': p->do_scan = p->do_restore = sox_true; break;
+      case 'h': p->make_headroom = sox_true; break;
+      default: lsx_fail("invalid option `-%c'", *q); return lsx_usage(effp);
+    }
+  if ((p->do_equalise + p->do_balance + p->do_balance_no_clip + p->do_restore)/ sox_true > 1) {
+    lsx_fail("Only one of -e, -B, -b, -r may be given");
+    return SOX_EOF;
+  }
+  if (p->do_normalise && p->do_restore) {
+    lsx_fail("Only one of -n, -r may be given");
+    return SOX_EOF;
+  }
+  do {NUMERIC_PARAMETER(fixed_gain, -HUGE_VAL, HUGE_VAL)} while (0);
+  p->fixed_gain = dB_to_linear(p->fixed_gain);
+  return argc? lsx_usage(effp) : SOX_SUCCESS;
+}
+
+static int start(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+
+  if (effp->flow == 0) {
+    if (p->do_restore) {
+      if (!effp->in_signal.mult || *effp->in_signal.mult >= 1) {
+        lsx_fail("can't restore level");
+        return SOX_EOF;
+      }
+      p->restore = 1 / *effp->in_signal.mult;
+    }
+    effp->out_signal.mult = p->make_headroom? &p->fixed_gain : NULL;
+    if (!p->do_equalise && !p->do_balance && !p->do_balance_no_clip)
+      effp->flows = 1;
+  }
+  p->mult = p->max = p->min = 0;
+  if (p->do_scan) {
+    p->tmp_file = lsx_tmpfile();
+    if (p->tmp_file == NULL) {
+      lsx_fail("can't create temporary file: %s", strerror(errno));
+      return SOX_EOF;
+    }
+  }
+  return SOX_SUCCESS;
+}
+
+static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
+    sox_sample_t * obuf, size_t * isamp, size_t * osamp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  size_t len;
+
+  if (p->do_scan) {
+    if (fwrite(ibuf, sizeof(*ibuf), *isamp, p->tmp_file) != *isamp) {
+      lsx_fail("error writing temporary file: %s", strerror(errno));
+      return SOX_EOF;
+    }
+    if (p->do_balance && !p->do_normalise)
+      for (len = *isamp; len; --len, ++ibuf) {
+        double d = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf);
+        p->rms += sqr(d);
+        ++p->num_samples;
+      }
+    else if (p->do_balance || p->do_balance_no_clip)
+      for (len = *isamp; len; --len, ++ibuf) {
+        double d = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf);
+        p->rms += sqr(d);
+        ++p->num_samples;
+        p->max = max(p->max, *ibuf);
+        p->min = min(p->min, *ibuf);
+      }
+    else for (len = *isamp; len; --len, ++ibuf) {
+      p->max = max(p->max, *ibuf);
+      p->min = min(p->min, *ibuf);
+    }
+    *osamp = 0; /* samples not output until drain */
+  }
+  else {
+    double mult = ((priv_t *)(effp - effp->flow)->priv)->fixed_gain;
+    len = *isamp = *osamp = min(*isamp, *osamp);
+    for (; len; --len, ++ibuf)
+      *obuf++ = SOX_ROUND_CLIP_COUNT(*ibuf * mult, effp->clips);
+  }
+  return SOX_SUCCESS;
+}
+
+static void start_drain(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  double max = SOX_SAMPLE_MAX, max_peak = 0, max_rms = 0;
+  size_t i;
+
+  if (p->do_balance || p->do_balance_no_clip) {
+    for (i = 0; i < effp->flows; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      max_rms = max(max_rms, sqrt(q->rms / q->num_samples));
+      rewind(q->tmp_file);
+    }
+    for (i = 0; i < effp->flows; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      double this_rms = sqrt(q->rms / q->num_samples);
+      double this_peak = max(q->max / max, q->min / (double)SOX_SAMPLE_MIN);
+      q->mult = this_rms != 0? max_rms / this_rms : 1;
+      max_peak = max(max_peak, q->mult * this_peak);
+      q->mult *= p->fixed_gain;
+    }
+    if (p->do_normalise || (p->do_balance_no_clip && max_peak > 1))
+      for (i = 0; i < effp->flows; ++i) {
+        priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+        q->mult /= max_peak;
+      }
+  } else if (p->do_equalise && !p->do_normalise) {
+    for (i = 0; i < effp->flows; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      double this_peak = max(q->max / max, q->min / (double)SOX_SAMPLE_MIN);
+      max_peak = max(max_peak, this_peak);
+      q->mult = p->fixed_gain / this_peak;
+      rewind(q->tmp_file);
+    }
+    for (i = 0; i < effp->flows; ++i) {
+      priv_t * q = (priv_t *)(effp - effp->flow + i)->priv;
+      q->mult *= max_peak;
+    }
+  } else {
+    p->mult = min(max / p->max, (double)SOX_SAMPLE_MIN / p->min);
+    if (p->do_restore) {
+      if (p->restore > p->mult)
+        lsx_debug("%.3gdB not restored", linear_to_dB(p->restore / p->mult));
+      else p->mult = p->restore;
+    }
+    p->mult *= p->fixed_gain;
+    rewind(p->tmp_file);
+  }
+}
+
+static int drain(sox_effect_t * effp, sox_sample_t * obuf, size_t * osamp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  size_t len;
+  int result = SOX_SUCCESS;
+
+  if (p->do_scan) {
+    if (!p->mult)
+      start_drain(effp);
+    len = fread(obuf, sizeof(*obuf), *osamp, p->tmp_file);
+    if (len != *osamp && !feof(p->tmp_file)) {
+      lsx_fail("error reading temporary file: %s", strerror(errno));
+      result = SOX_EOF;
+    }
+    if (p->mult > 1) for (*osamp = len; len; --len, ++obuf)
+      *obuf = SOX_ROUND_CLIP_COUNT(*obuf * p->mult, effp->clips);
+    else for (*osamp = len; len; --len, ++obuf)
+      *obuf = floor(*obuf * p->mult + .5);
+  }
+  else *osamp = 0;
+  return result;
+}
+
+static int stop(sox_effect_t * effp)
+{
+  priv_t * p = (priv_t *)effp->priv;
+  if (p->do_scan)
+    fclose(p->tmp_file); /* auto-deleted by lsx_tmpfile */
+  return SOX_SUCCESS;
+}
+
+sox_effect_handler_t const * sox_gain_effect_fn(void)
+{
+  static sox_effect_handler_t handler = {
+    "gain", "[-e|-b|-B|-r] [-n] [-h] [gain-dB]", SOX_EFF_GAIN,
+    create, start, flow, drain, stop, NULL, sizeof(priv_t)};
+  return &handler;
+}
+
+/*------------------ emulation of the old `normalise' effect -----------------*/
+
+static int norm_getopts(sox_effect_t * effp, int argc, char * * argv)
+{
+  char * argv2[3];
+  int argc2 = 0;
+
+  argv2[argc2++] = argv[0], --argc, ++argv;
+  if (argc && !strcmp(*argv, "-i"))
+    argv2[argc2++] = "-en", --argc, ++argv;
+  else if (argc && !strcmp(*argv, "-b"))
+    argv2[argc2++] = "-B", --argc, ++argv;
+  if (argc2 > 1)
+    lsx_warn("this use is deprecated; use `gain %s' instead", argv2[1]);
+  else argv2[argc2++] = "-n";
+  if (argc)
+    argv2[argc2++] = *argv, --argc, ++argv;
+  return argc? lsx_usage(effp) :
+    sox_gain_effect_fn()->getopts(effp, argc2, argv2);
+}
+
+sox_effect_handler_t const * sox_norm_effect_fn(void)
+{
+  static sox_effect_handler_t handler;
+  handler = *sox_gain_effect_fn();
+  handler.name = "norm";
+  handler.usage = "[level]";
+  handler.getopts = norm_getopts;
+  return &handler;
+}
--- a/src/gsm.c
+++ b/src/gsm.c
@@ -195,10 +195,9 @@
 
         while (done < samp)
         {
-                SOX_SAMPLE_LOCALS;
                 while ((p->samplePtr < p->sampleTop) && (done < samp))
                         *(p->samplePtr)++ =
-                            SOX_SAMPLE_TO_SIGNED_16BIT(buf[done++], ft->clips);
+                            SOX_SAMPLE_TO_SIGNED_16BIT(buf[done++]);
 
                 if (p->samplePtr == p->sampleTop)
                 {
--- a/src/hcom.c
+++ b/src/hcom.c
@@ -277,9 +277,8 @@
   }
 
   for (i = 0; i < len; i++) {
-    SOX_SAMPLE_LOCALS;
     datum = *buf++;
-    p->data[p->pos++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(datum, ft->clips);
+    p->data[p->pos++] = SOX_SAMPLE_TO_UNSIGNED_8BIT(datum);
   }
 
   return len;
--- a/src/ladspa.c
+++ b/src/ladspa.c
@@ -237,7 +237,7 @@
     if (l_st->input_port != ULONG_MAX) {
       /* Copy the input; FIXME: Assume LADSPA_Data == float! */
       for (i = 0; i < len; i++)
-        buf[i] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i], effp->clips);
+        buf[i] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i]);
 
       /* Connect the input port */
       l_st->desc->connect_port(l_st->handle, l_st->input_port, buf);
@@ -291,7 +291,7 @@
 static sox_effect_handler_t sox_ladspa_effect = {
   "ladspa",
   "MODULE [PLUGIN] [ARGUMENT...]",
-  0,
+  SOX_EFF_GAIN,
   sox_ladspa_getopts,
   sox_ladspa_start,
   sox_ladspa_flow,
--- a/src/lpc10.c
+++ b/src/lpc10.c
@@ -179,8 +179,7 @@
 
   while (len > 0) {
     while (len > 0 && lpc->samples < LPC10_SAMPLES_PER_FRAME) {
-      SOX_SAMPLE_LOCALS;
-      lpc->speech[lpc->samples++] = SOX_SAMPLE_TO_FLOAT_32BIT(buf[nwritten++], ft->clips);
+      lpc->speech[lpc->samples++] = SOX_SAMPLE_TO_FLOAT_32BIT(buf[nwritten++]);
       len--;
     }
 
--- a/src/mcompand.c
+++ b/src/mcompand.c
@@ -492,7 +492,7 @@
     "  attack1,decay1[,attack2,decay2...]\n"
     "                 in-dB1,out-dB1[,in-dB2,out-dB2...]\n"
     "                [ gain [ initial-volume [ delay ] ] ]",
-    SOX_EFF_MCHAN,
+    SOX_EFF_MCHAN | SOX_EFF_GAIN,
     getopts, start, flow, drain, stop, kill, sizeof(priv_t)
   };
 
--- a/src/mixer.c
+++ b/src/mixer.c
@@ -485,6 +485,19 @@
          }
      }
 
+     if (effp->in_signal.mult) {
+       double max_sum = 0;
+
+       for (j = 0; j < (int)effp->out_signal.channels; ++j) {
+         double sum = 0;
+         for (i = 0; i < (int)effp->in_signal.channels; ++i)
+           sum += fabs(mixer->sources[mixer->mix == MIX_CENTER? 0 : i][j]);
+         max_sum = max(max_sum, sum);
+       }
+       if (max_sum > 1)
+         *effp->in_signal.mult /= max_sum;
+     }
+
      if (effp->in_signal.channels != effp->out_signal.channels)
        return SOX_SUCCESS;
 
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -406,7 +406,6 @@
     int i,j;
     ptrdiff_t done = 0;
     size_t written;
-    SOX_SAMPLE_LOCALS;
 
     /* NOTE: This logic assumes that "short int" is 16-bits
      * on all platforms.  It happens to be for all that I know
@@ -435,8 +434,8 @@
         j=0;
         for (i=0; i<nsamples; i++)
         {
-            buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips);
-            buffer_r[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips);
+            buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++]);
+            buffer_r[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++]);
         }
     }
     else
@@ -443,7 +442,7 @@
     {
         j=0;
         for (i=0; i<nsamples; i++)
-            buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++], ft->clips);
+            buffer_l[i]=SOX_SAMPLE_TO_SIGNED_16BIT(buf[j++]);
     }
 
     mp3buffer_size = 1.25 * nsamples + 7200;
--- a/src/noiseprof.c
+++ b/src/noiseprof.c
@@ -116,7 +116,7 @@
                     size_t *isamp, size_t *osamp)
 {
   priv_t * p = (priv_t *) effp->priv;
-  size_t samp = min(*isamp, *osamp), dummy = 0; /* No need to clip count */
+  size_t samp = min(*isamp, *osamp);
   size_t chans = effp->in_signal.channels;
   size_t i, j, n = min(samp / chans, WINDOWSIZE - p->bufdata);
 
@@ -125,11 +125,10 @@
 
   /* Collect data for every channel. */
   for (i = 0; i < chans; i ++) {
-    SOX_SAMPLE_LOCALS;
     chandata_t * chan = &(p->chandata[i]);
     for (j = 0; j < n; j ++)
       chan->window[j + p->bufdata] =
-        SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i + j * chans], dummy);
+        SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i + j * chans]);
     if (n + p->bufdata == WINDOWSIZE)
       collect_data(chan);
   }
@@ -208,7 +207,7 @@
 static sox_effect_handler_t sox_noiseprof_effect = {
   "noiseprof",
   "[profile-file]",
-  SOX_EFF_MCHAN,
+  SOX_EFF_MCHAN | SOX_EFF_MODIFY,
   sox_noiseprof_getopts,
   sox_noiseprof_start,
   sox_noiseprof_flow,
--- a/src/noisered.c
+++ b/src/noisered.c
@@ -285,7 +285,6 @@
 
     /* Reduce noise on every channel. */
     for (i = 0; i < tracks; i ++) {
-        SOX_SAMPLE_LOCALS;
         chandata_t* chan = &(data->chandata[i]);
         size_t j;
 
@@ -294,7 +293,7 @@
 
         for (j = 0; j < ncopy; j ++)
             chan->window[oldbuf + j] =
-                SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i + tracks * j], effp->clips);
+                SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[i + tracks * j]);
 
         if (!whole_window)
             continue;
--- a/src/normalise.c
+++ /dev/null
@@ -1,127 +1,0 @@
-/* libSoX effect: Normalise   (c) 2008 robs@users.sourceforge.net
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation; either version 2.1 of the License, or (at
- * your option) any later version.
- *
- * This library is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
- * General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this library; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
- */
-
-#include "sox_i.h"
-#include <string.h>
-
-typedef struct {
-  sox_bool      individual, balance;
-  double        norm0;    /* Multiplier to take to 0dB FSD */
-  double        level;    /* Multiplier to take to 'level' */
-  double        rms;
-  off_t         num_samples;
-  sox_sample_t  min, max;
-  FILE          * tmp_file;
-} priv_t;
-
-static int create(sox_effect_t * effp, int argc, char * * argv)
-{
-  priv_t * p = (priv_t *)effp->priv;
-  --argc, ++argv;
-  if (argc && !strcmp(*argv, "-i")) p->individual = sox_true, ++argv, --argc;
-  if (argc && !strcmp(*argv, "-b")) p->balance = sox_true, ++argv, --argc;
-  do {NUMERIC_PARAMETER(level, -100, 0)} while (0);
-  p->level = dB_to_linear(p->level);
-  return argc?  lsx_usage(effp) : SOX_SUCCESS;
-}
-
-static int start(sox_effect_t * effp)
-{
-  priv_t * p = (priv_t *)effp->priv;
-  if (!(p->individual || p->balance))
-    effp->flows = 1;
-  p->norm0 = p->max = p->min = 0;
-  p->tmp_file = lsx_tmpfile();
-  if (p->tmp_file == NULL) {
-    lsx_fail("can't create temporary file: %s", strerror(errno));
-    return SOX_EOF;
-  }
-  return SOX_SUCCESS;
-}
-
-static int flow(sox_effect_t * effp, const sox_sample_t * ibuf,
-    sox_sample_t * obuf, size_t * isamp, size_t * osamp)
-{
-  priv_t * p = (priv_t *)effp->priv;
-  size_t len;
-
-  if (fwrite(ibuf, sizeof(*ibuf), *isamp, p->tmp_file) != *isamp) {
-    lsx_fail("error writing temporary file: %s", strerror(errno));
-    return SOX_EOF;
-  }
-  if (p->balance) for (len = *isamp; len; --len, ++ibuf) {
-    SOX_SAMPLE_LOCALS;
-    size_t dummy = 0;
-    double d = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf, dummy);
-    p->rms += sqr(d);
-    ++p->num_samples;
-  }
-  else for (len = *isamp; len; --len, ++ibuf) {
-    p->max = max(p->max, *ibuf);
-    p->min = min(p->min, *ibuf);
-  }
-  (void)obuf, *osamp = 0; /* samples not output until drain */
-  return SOX_SUCCESS;
-}
-
-static int drain(sox_effect_t * effp, sox_sample_t * obuf, size_t * osamp)
-{
-  priv_t * p = (priv_t *)effp->priv;
-  size_t len, i;
-  int result = SOX_SUCCESS;
-
-  if (!p->norm0) {
-    if (p->balance) {
-      double max_rms = 0, this_rms = sqrt(p->rms / p->num_samples);
-      sox_effect_t * effects = effp - effp->flow;
-      for (i = 0; i < effp->flows; ++i) {
-        priv_t * q = (priv_t *)(effects + i)->priv;
-        max_rms = max(max_rms, sqrt(q->rms / q->num_samples));
-      }
-      p->norm0 = p->level * (this_rms != 0? max_rms / this_rms : 1);
-    }
-    else {
-      double max = lsx_sample_max(effp->out_encoding);
-      p->norm0 = p->level * min(max / p->max, (double)SOX_SAMPLE_MIN / p->min);
-    }
-    rewind(p->tmp_file);
-  }
-  len = fread(obuf, sizeof(*obuf), *osamp, p->tmp_file);
-  if (len != *osamp && !feof(p->tmp_file)) {
-    lsx_fail("error reading temporary file: %s", strerror(errno));
-    result = SOX_EOF;
-  }
-  if (p->balance) for (*osamp = len; len; --len, ++obuf)
-    *obuf = SOX_ROUND_CLIP_COUNT(*obuf * p->norm0, effp->clips);
-  else for (*osamp = len; len; --len, ++obuf)
-    *obuf = floor(*obuf * p->norm0 + .5);
-  return result;
-}
-
-static int stop(sox_effect_t * effp)
-{
-  priv_t * p = (priv_t *)effp->priv;
-  fclose(p->tmp_file); /* auto-deleted by lsx_tmpfile */
-  return SOX_SUCCESS;
-}
-
-sox_effect_handler_t const * sox_norm_effect_fn(void)
-{
-  static sox_effect_handler_t handler = {"norm", "[-i|-b] [level]", 0,
-    create, start, flow, drain, stop, NULL, sizeof(priv_t)};
-  return &handler;
-}
--- a/src/overdrive.c
+++ b/src/overdrive.c
@@ -52,7 +52,7 @@
   size_t dummy = 0, len = *isamp = *osamp = min(*isamp, *osamp);
   while (len--) {
     SOX_SAMPLE_LOCALS;
-    double d = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf++, dummy), d0 = d;
+    double d = SOX_SAMPLE_TO_FLOAT_64BIT(*ibuf++), d0 = d;
     d *= p->gain;
     d += p->colour;
     d = d < -1? -2./3 : d > 1? 2./3 : d - d * d * d * (1./3);
@@ -66,6 +66,6 @@
 sox_effect_handler_t const * sox_overdrive_effect_fn(void)
 {
   static sox_effect_handler_t handler = {"overdrive", "[gain [colour]]",
-    0, create, start, flow, NULL, NULL, NULL, sizeof(priv_t)};
+    SOX_EFF_GAIN, create, start, flow, NULL, NULL, NULL, sizeof(priv_t)};
   return &handler;
 }
--- a/src/phaser.c
+++ b/src/phaser.c
@@ -139,7 +139,7 @@
 {
   static sox_effect_handler_t handler = {
     "phaser", "gain-in gain-out delay decay speed [ -s | -t ]",
-    SOX_EFF_LENGTH, getopts, start, flow, NULL, stop, NULL, sizeof(priv_t)
+    SOX_EFF_LENGTH | SOX_EFF_GAIN, getopts, start, flow, NULL, stop, NULL, sizeof(priv_t)
   };
   return &handler;
 }
--- a/src/rate.c
+++ b/src/rate.c
@@ -494,7 +494,7 @@
   if (effp->in_signal.rate == out_rate)
     return SOX_EFF_NULL;
 
-  if (effp->flow == 0 && effp->in_signal.mult)
+  if (effp->in_signal.mult)
     *effp->in_signal.mult *= .705; /* 1/(2/sinc(pi/3)-1); see De Soras 4.1.2 */
 
   effp->out_signal.channels = effp->in_signal.channels;
@@ -516,7 +516,7 @@
 
   if (*isamp && odone < *osamp) {
     sample_t * t = rate_input(&p->rate, NULL, *isamp);
-    for (i = *isamp; i; --i) *t++ = FROM_SOX(*ibuf++, effp->clips);
+    for (i = *isamp; i; --i) *t++ = FROM_SOX(*ibuf++);
     rate_process(&p->rate);
   }
   else *isamp = 0;
--- a/src/raw.c
+++ b/src/raw.c
@@ -14,8 +14,8 @@
 typedef uint16_t uint13_t;
 #define SOX_ULAW_BYTE_TO_SAMPLE(d,clips)   SOX_SIGNED_16BIT_TO_SAMPLE(sox_ulaw2linear16(d),clips)
 #define SOX_ALAW_BYTE_TO_SAMPLE(d,clips)   SOX_SIGNED_16BIT_TO_SAMPLE(sox_alaw2linear16(d),clips)
-#define SOX_SAMPLE_TO_ULAW_BYTE(d,c) sox_14linear2ulaw(SOX_SAMPLE_TO_UNSIGNED(14,d,c) - 0x2000)
-#define SOX_SAMPLE_TO_ALAW_BYTE(d,c) sox_13linear2alaw(SOX_SAMPLE_TO_UNSIGNED(13,d,c) - 0x1000)
+#define SOX_SAMPLE_TO_ULAW_BYTE(d) sox_14linear2ulaw(SOX_SAMPLE_TO_UNSIGNED(14,d) - 0x2000)
+#define SOX_SAMPLE_TO_ALAW_BYTE(d) sox_13linear2alaw(SOX_SAMPLE_TO_UNSIGNED(13,d) - 0x1000)
 
 int lsx_rawseek(sox_format_t * ft, uint64_t offset)
 {
@@ -95,11 +95,10 @@
   static size_t sox_write_ ## sign ## type ## _samples( \
       sox_format_t * ft, sox_sample_t const * buf, size_t len) \
   { \
-    SOX_SAMPLE_LOCALS; \
     size_t n, nwritten; \
     ctype *data = lsx_malloc(sizeof(ctype) * len); \
     for (n = 0; n < len; n++) \
-      data[n] = cast(buf[n], ft->clips); \
+      data[n] = cast(buf[n]); \
     nwritten = lsx_write_ ## type ## _buf(ft, (uctype *)data, len); \
     free(data); \
     return nwritten; \
--- a/src/remix.c
+++ b/src/remix.c
@@ -95,6 +95,19 @@
         p->out_specs[i].in_specs[j].multiplier = (p->mode == automatic || (p->mode == semi && !mul_spec)) ? mult : 1;
   }
   effp->out_signal.channels = p->num_out_channels;
+
+  if (effp->in_signal.mult) {
+    double max_sum = 0;
+
+    for (j = 0; j < effp->out_signal.channels; j++) {
+      double sum = 0;
+      for (i = 0; i < p->out_specs[j].num_in_channels; i++)
+        sum += fabs(p->out_specs[j].in_specs[i].multiplier);
+      max_sum = max(max_sum, sum);
+    }
+    if (max_sum > 1)
+      *effp->in_signal.mult /= max_sum;
+  }
   return SOX_SUCCESS;
 }
 
--- a/src/reverb.c
+++ b/src/reverb.c
@@ -212,6 +212,9 @@
     &p->chan[i].reverb, effp->in_signal.rate, p->wet_gain_dB, p->room_scale,
     p->reverberance, p->hf_damping, p->pre_delay_ms, p->stereo_depth,
     effp->global_info->global_info->bufsiz / p->ochannels, p->chan[i].wet);
+
+  if (effp->in_signal.mult)
+    *effp->in_signal.mult /= !p->wet_only + 2 * dB_to_linear(max(0,p->wet_gain_dB));
   return SOX_SUCCESS;
 }
 
@@ -226,7 +229,7 @@
   for (c = 0; c < p->ichannels; ++c)
     p->chan[c].dry = fifo_write(&p->chan[c].reverb.input_fifo, len, 0);
   for (i = 0; i < len; ++i) for (c = 0; c < p->ichannels; ++c)
-    p->chan[c].dry[i] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++, effp->clips);
+    p->chan[c].dry[i] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++);
   for (c = 0; c < p->ichannels; ++c)
     reverb_process(&p->chan[c].reverb, len);
   if (p->ichannels == 2) for (i = 0; i < len; ++i) for (w = 0; w < 2; ++w) {
--- a/src/silence.c
+++ b/src/silence.c
@@ -275,27 +275,25 @@
 {
     double ratio;
     int rc;
-    sox_sample_t dummy_clipped_count = 0;
 
     /* When scaling low bit data, noise values got scaled way up */
     /* Only consider the original bits when looking for silence */
     switch(effp->in_signal.precision)
     {
-        SOX_SAMPLE_LOCALS;
         case 8:
-            value = SOX_SAMPLE_TO_SIGNED_8BIT(value, dummy_clipped_count);
+            value = SOX_SAMPLE_TO_SIGNED_8BIT(value);
             ratio = (double)abs(value) / (double)SOX_INT8_MAX;
             break;
         case 16:
-            value = SOX_SAMPLE_TO_SIGNED_16BIT(value, dummy_clipped_count);
+            value = SOX_SAMPLE_TO_SIGNED_16BIT(value);
             ratio = (double)abs(value) / (double)SOX_INT16_MAX;
             break;
         case 24:
-            value = SOX_SAMPLE_TO_SIGNED_24BIT(value, dummy_clipped_count);
+            value = SOX_SAMPLE_TO_SIGNED_24BIT(value);
             ratio = (double)abs(value) / (double)SOX_INT24_MAX;
             break;
         case 32:
-            value = SOX_SAMPLE_TO_SIGNED_32BIT(value,);
+            value = SOX_SAMPLE_TO_SIGNED_32BIT(value);
             ratio = (double)abs(value) / (double)SOX_INT32_MAX;
             break;
         default:
--- a/src/skelform.c
+++ b/src/skelform.c
@@ -159,9 +159,8 @@
   switch (ft->encoding.bits_per_sample) {
   case 8:
     switch (ft->encoding.encoding) {
-    SOX_SAMPLE_LOCALS;
     case SOX_ENCODING_UNSIGNED:
-      while (done < len && lsx_writeb(ft, SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips)) == SOX_SUCCESS)
+      while (done < len && lsx_writeb(ft, SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++)) == SOX_SUCCESS)
         ++done;
       break;
     default:
--- a/src/smp.c
+++ b/src/smp.c
@@ -368,8 +368,7 @@
         size_t done = 0;
 
         while(done < len) {
-                SOX_SAMPLE_LOCALS;
-                datum = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
+                datum = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++);
                 lsx_writew(ft, (uint16_t)datum);
                 smp->NoOfSamps++;
                 done++;
--- a/src/sox.c
+++ b/src/sox.c
@@ -1,9 +1,9 @@
 /* SoX - The Swiss Army Knife of Audio Manipulation.
  *
- * This is the main function for the command line sox program.
+ * This is the main function for the SoX command line programs.
  *
+ * Copyright 1998-2009 Chris Bagwell and SoX contributors
  * Copyright 1991 Lance Norskog And Sundry Contributors
- * Copyright 1998-2008 Chris Bagwell and SoX contributors
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License as published by the
@@ -133,6 +133,7 @@
   sox_signalinfo_t signal;
   sox_encodinginfo_t encoding;
   double volume;
+  sox_sample_t offset;
   double replay_gain;
   sox_oob_t oob;
   sox_bool no_glob;
@@ -197,9 +198,8 @@
 static struct termios original_termios;
 #endif
 
-static sox_bool stdin_is_a_tty, is_player;
+static sox_bool stdin_is_a_tty, is_player, is_guarded, do_guarded_norm;
 
-
 /* Cleanup atexit() function, hence always called. */
 static void cleanup(void)
 {
@@ -214,7 +214,7 @@
   }
 
   if (file_count) {
-    if (ofile->ft) {                  
+    if (ofile->ft) {
       if (!success && ofile->ft->fp) {   /* If we failed part way through */
         struct stat st;                  /* writing a normal file, remove it. */
         fstat(fileno(ofile->ft->fp), &st);
@@ -405,6 +405,8 @@
     display_file_info(f->ft, f, sox_false);
   if (f->volume == HUGE_VAL)
     f->volume = 1;
+  if (f->encoding.half_bit && f->ft->signal.precision < 32)
+    f->offset = 1 << (31 - f->ft->signal.precision);
   if (f->replay_gain != HUGE_VAL)
     f->volume *= pow(10.0, f->replay_gain / 20);
   f->ft->sox_errno = errno = 0;
@@ -411,7 +413,6 @@
 }
 
 /* Read up to max `wide' samples.  A wide sample contains one sample per channel
-            printf("effect %d obeg = %d oend = %d\n", e, effects_chain->effects[e]->obeg, effects_chain->effects[e]->oend);
  * from the input audio. */
 static size_t sox_read_wide(sox_format_t * ft, sox_sample_t * buf, size_t max)
 {
@@ -427,11 +428,12 @@
 {
   size_t s = ws * f->ft->signal.channels;
 
-  if (f->volume != 1)
-    while (s--) {
-      double d = f->volume * *buf;
-      *buf++ = SOX_ROUND_CLIP_COUNT(d, f->volume_clips);
-    }
+  if (f->volume == 1 && f->offset !=0 ) while (s--)
+    *buf++ -= f->offset;
+  else if (f->volume != 1 || f->offset !=0 ) while (s--) {
+    double d = f->volume * (*buf - f->offset);
+    *buf++ = SOX_ROUND_CLIP_COUNT(d, f->volume_clips);
+  }
 }
 
 /* The input combiner: contains one sample buffer per input file, but only
@@ -557,12 +559,24 @@
   return &handler;
 }
 
+static int ostart(sox_effect_t *effp)
+{
+  (void)effp;
+  if (effp->out_encoding->half_bit && effp->out_signal.precision < 32)
+    ofile->offset = 1 << (31 - ofile->ft->signal.precision);
+  return SOX_SUCCESS;
+}
+
 static int output_flow(sox_effect_t *effp, sox_sample_t const * ibuf,
     sox_sample_t * obuf, size_t * isamp, size_t * osamp)
 {
+  sox_sample_t const * buf;
   size_t len;
 
   (void)effp, (void)obuf;
+  if (ofile->offset != 0 ) for (buf = ibuf, len = *isamp; len; --len, ++buf)
+    *(sox_sample_t *)buf = *buf >= SOX_SAMPLE_MAX - ofile->offset?
+      SOX_SAMPLE_MAX : *buf + ofile->offset;
   if (show_progress) for (len = 0; len < *isamp; len += effp->in_signal.channels) {
     omax[0] = max(omax[0], ibuf[len]);
     omin[0] = min(omin[0], ibuf[len]);
@@ -591,26 +605,49 @@
 static sox_effect_handler_t const * output_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "output", 0, SOX_EFF_MCHAN, NULL, NULL, output_flow, NULL, NULL, NULL, 0
+    "output", 0, SOX_EFF_MCHAN, NULL, ostart, output_flow, NULL, NULL, NULL, 0
   };
   return &handler;
 }
 
-static void add_effect(sox_effects_chain_t *chain, char const *name, 
-                       int argc, char *argv[], sox_signalinfo_t *signal)
+static void auto_effect(sox_effects_chain_t *, char const *, int, char **,
+    sox_signalinfo_t *, int *);
+
+static int add_effect(sox_effects_chain_t * chain, sox_effect_t * effp,
+    sox_signalinfo_t * in, sox_signalinfo_t const * out, int * guard) {
+  int no_guard = -1;
+  switch (*guard) {
+    case 0: if (!(effp->handler.flags & SOX_EFF_GAIN)) {
+      char * arg = "-h";
+      auto_effect(chain, "gain", 1, &arg, in, &no_guard);
+      ++*guard;
+    }
+    break;
+    case 1: if (effp->handler.flags & SOX_EFF_GAIN) {
+      char * arg = "-r";
+      auto_effect(chain, "gain", 1, &arg, in, &no_guard);
+      --*guard;
+    }
+    break;
+    case 2: if (!(effp->handler.flags & SOX_EFF_MODIFY)) {
+      lsx_fail("effects that modify audio must not follow dither");
+      exit(1);
+    }
+  }
+  return sox_add_effect(chain, effp, in, out);
+}
+
+static void auto_effect(sox_effects_chain_t *chain, char const *name, int argc,
+    char *argv[], sox_signalinfo_t *signal, int * guard)
 {
   sox_effect_t * effp;
 
   effp = sox_create_effect(sox_find_effect(name)); /* Should always succeed. */
 
-  if (effp->handler.flags & SOX_EFF_DEPRECATED)
-    lsx_warn("effect `%s' is deprecated; see sox(1) for an alternative", 
-             effp->handler.name);
-
   if (sox_effect_options(effp, argc, argv) == SOX_EOF)
     exit(1); /* The failing effect should have displayed an error message */
-  
-  if (sox_add_effect(chain, effp, signal, &ofile->ft->signal) != SOX_SUCCESS)
+
+  if (add_effect(chain, effp, signal, &ofile->ft->signal, guard) != SOX_SUCCESS)
     exit(2); /* The effects chain should have displayed an error message */
 }
 
@@ -621,7 +658,7 @@
   nuser_effects[0] = 0;
 } /* init_eff_chains */
 
-/* add_eff_chain() - NOTE: this only adds memory for one 
+/* add_eff_chain() - NOTE: this only adds memory for one
  * additional effects chain beyond value of eff_chain_count.  It
  * does not unconditionally increase size of effects chain.
  */
@@ -639,9 +676,9 @@
   unsigned j;
   int i, k;
 
-  for (i = 0; i < eff_chain_count; i++) 
+  for (i = 0; i < eff_chain_count; i++)
   {
-    for (j = 0; j < nuser_effects[i]; j++) 
+    for (j = 0; j < nuser_effects[i]; j++)
     {
       if (user_effargs[i][j].name)
         free(user_effargs[i][j].name);
@@ -665,7 +702,7 @@
 
 static sox_bool is_pseudo_effect(char *s)
 {
-  if (strcmp("newfile", s) == 0 || 
+  if (strcmp("newfile", s) == 0 ||
       strcmp("restart", s) == 0 ||
       strcmp(":", s) == 0)
     return sox_true;
@@ -700,7 +737,7 @@
       optind++;
       continue;
     }
-    
+
     if (strcmp(argv[optind], "newfile") == 0)
     {
       /* Start a new effect chain for newfile if user doesn't
@@ -739,7 +776,7 @@
 
     optind += j; /* Skip past the effect arguments */
     nuser_effects[eff_chain_count]++;
-    if (newline_mode) 
+    if (newline_mode)
     {
       output_method = sox_multiple;
       eff_chain_count++;
@@ -865,9 +902,9 @@
 /* Creates users effects and passes in user specified options.
  * This is done without putting anything into the effects chain
  * because an effect may set the effp->in_format and we may want
- * to copy that back into the input/combiner before opening and 
- * inserting it.  
- * Similarly, we may want to use effp->out_format to override the 
+ * to copy that back into the input/combiner before opening and
+ * inserting it.
+ * Similarly, we may want to use effp->out_format to override the
  * default values of output file before we open it.
  * To keep things simple, we create all user effects.  Later, when
  * we add them, some may already be in the chain and we will need to free
@@ -882,11 +919,11 @@
     effp = sox_create_effect(sox_find_effect(user_effargs[current_eff_chain][i].name));
 
     if (effp->handler.flags & SOX_EFF_DEPRECATED)
-      lsx_warn("effect `%s' is deprecated; see sox(1) for an alternative", 
+      lsx_warn("effect `%s' is deprecated; see sox(1) for an alternative",
           effp->handler.name);
 
     /* The failing effect should have displayed an error message */
-    if (sox_effect_options(effp, user_effargs[current_eff_chain][i].argc, 
+    if (sox_effect_options(effp, user_effargs[current_eff_chain][i].argc,
           user_effargs[current_eff_chain][i].argv) == SOX_EOF)
       exit(1);
 
@@ -905,6 +942,7 @@
 static void add_effects(sox_effects_chain_t *chain)
 {
   sox_signalinfo_t signal = combiner_signal;
+  int guard = is_guarded - 1;
   unsigned i;
   sox_effect_t * effp;
   char * rate_arg =
@@ -911,32 +949,43 @@
     is_player ? (rate_arg = getenv("PLAY_RATE_ARG")) ? rate_arg : "-l" : NULL;
 
   /* 1st `effect' in the chain is the input combiner_signal.
-   * add it only if its not there from a previous run.
-   */
-  if (chain->length == 0)
-  {
+   * add it only if its not there from a previous run.  */
+  if (chain->length == 0) {
     effp = sox_create_effect(input_combiner_effect_fn());
     sox_add_effect(chain, effp, &signal, &ofile->ft->signal);
   }
 
-  /* Add auto effects if appropriate; add user specified effects */
-  for (i = 0; i < nuser_effects[current_eff_chain]; i++) {
-    /* Effects chain should have displayed an error message */
-    if (sox_add_effect(chain, user_efftab[i], 
-                       &signal, &ofile->ft->signal) != SOX_SUCCESS)
-      exit(2);
-  }
+  /* Add user specified effects; stop before `dither' */
+  for (i = 0; i < nuser_effects[current_eff_chain] &&
+      strcmp(user_efftab[i]->handler.name, "dither"); i++)
+    if (add_effect(chain, user_efftab[i], &signal, &ofile->ft->signal,
+          &guard) != SOX_SUCCESS)
+      exit(2); /* Effects chain should have displayed an error message */
 
   /* Add auto effects if still needed at this point */
+  if (signal.channels < ofile->ft->signal.channels &&
+      signal.rate != ofile->ft->signal.rate)
+    auto_effect(chain, "rate", rate_arg != NULL, &rate_arg, &signal, &guard);
+  if (signal.channels != ofile->ft->signal.channels)
+    auto_effect(chain, "mixer", 0, NULL, &signal, &guard);
   if (signal.rate != ofile->ft->signal.rate)
-  {
-    add_effect(chain, "rate", rate_arg != NULL, &rate_arg, &signal); /* Must be up-sampling */
+    auto_effect(chain, "rate", rate_arg != NULL, &rate_arg, &signal, &guard);
+
+  if (is_guarded && (do_guarded_norm || !(signal.mult && *signal.mult == 1))) {
+    char * arg = do_guarded_norm? "-nh" : guard? "-rh" : "-h";
+    int no_guard = -1;
+    auto_effect(chain, "gain", 1, &arg, &signal, &no_guard);
+    guard = 1;
   }
-  if (signal.channels != ofile->ft->signal.channels)
-  {
-    add_effect(chain, "mixer", 0, NULL, &signal); /* Must be increasing channels */
+
+  /* Add user specified effects from `dither' onwards */
+  for (; i < nuser_effects[current_eff_chain]; i++, guard = 2) {
+    if (add_effect(chain, user_efftab[i], &signal, &ofile->ft->signal,
+          &guard) != SOX_SUCCESS)
+      exit(2); /* Effects chain should have displayed an error message */
+    chain->out_enc->half_bit = sox_false;
   }
-    
+
   if (!save_output_eff)
   {
     /* Last `effect' in the chain is the output file */
@@ -981,15 +1030,15 @@
     /* Effect chain stopped so advance to next effect chain but
      * quite if no more chains exist.
      */
-    else if (++current_eff_chain >= eff_chain_count) 
+    else if (++current_eff_chain >= eff_chain_count)
       return SOX_EOF;
 
-    while (nuser_effects[current_eff_chain] == 1 && 
+    while (nuser_effects[current_eff_chain] == 1 &&
            is_pseudo_effect(user_effargs[current_eff_chain][0].name))
     {
       if (strcmp("newfile", user_effargs[current_eff_chain][0].name) == 0)
       {
-        if (++current_eff_chain >= eff_chain_count) 
+        if (++current_eff_chain >= eff_chain_count)
           return SOX_EOF;
         reuse_output = sox_false;
       }
@@ -1167,7 +1216,7 @@
     int ch = getchar();
 
 #ifdef INTERACTIVE
-    if (files[current_input]->ft->handler.seek && 
+    if (files[current_input]->ft->handler.seek &&
         files[current_input]->ft->seekable)
     {
       if (ch == '>')
@@ -1408,7 +1457,7 @@
    */
   for (i = 0; i < input_count; i++) {
     unsigned j;
-    for (j =0; j < nuser_effects[current_eff_chain] && 
+    for (j =0; j < nuser_effects[current_eff_chain] &&
                !files[i]->ft->signal.channels; ++j)
       files[i]->ft->signal.channels = user_efftab[j]->in_signal.channels;
     /* For historical reasons, default to one channel if not specified. */
@@ -1520,7 +1569,7 @@
   /* Determine the output file encoding attributes; set from user options
    * if given: */
   ofile->encoding = ofile_encoding_options;
- 
+
   /* Get unspecified output file encoding attributes from the input file and
    * set the output file to the resultant encoding if this is supported by the
    * output file type; if not, the output file handler should select an
@@ -1548,7 +1597,7 @@
   open_output_file();
 
   if (!effects_chain)
-    effects_chain = sox_create_effects_chain(&combiner_encoding, 
+    effects_chain = sox_create_effects_chain(&combiner_encoding,
                                              &ofile->ft->encoding);
   add_effects(effects_chain);
 
@@ -1564,7 +1613,7 @@
    *    effect chains.
    * For case #2, something else must decide when to stop processing.
    */
-  if ((input_eof && current_input < input_count) || 
+  if ((input_eof && current_input < input_count) ||
       (!output_eof && current_eff_chain < eff_chain_count))
     flow_status = SOX_SUCCESS;
 
@@ -1691,6 +1740,7 @@
 "--combine concatenate    Concatenate multiple input files (default for sox, rec)",
 "--combine sequence       Sequence multiple input files (default for play)",
 "--effects-file FILENAME  File containing effects and options",
+"-G, --guard              Use temporary files to guard against clipping",
 "-h, --help               Display version number and usage information",
 "--help-effect NAME       Show usage of effect NAME, or NAME=all for all",
 "--help-format NAME       Show info on format NAME, or NAME=all for all",
@@ -1698,6 +1748,7 @@
 "--interactive            Prompt to overwrite output file",
 "-m, --combine mix        Mix multiple input files (instead of concatenating)",
 "-M, --combine merge      Merge multiple input files (instead of concatenating)",
+"--norm                   Guard (see --guard) & normalise",
 "--plot gnuplot|octave    Generate script to plot response of filter effect",
 "-q, --no-show-progress   Run in quiet mode; opposite of -S",
 "--replay-gain track|album|off  Default: off (sox, rec), track (play)",
@@ -1728,6 +1779,7 @@
 "-X|--reverse-bits        Encoded bit-order",
 "--endian little|big|swap Encoded byte-order; swap means opposite to default",
 "-L/-B/-x                 Short options for the above",
+"-0                       PCM encoding is 0 biased (offset by half a bit)",
 "-c|--channels CHANNELS   Number of channels of audio data; e.g. 2 = stereo",
 "-r|--rate RATE           Sample rate of audio",
 "-C|--compression FACTOR  Compression factor for output format",
@@ -1885,7 +1937,7 @@
   free(text);
 }
 
-static char *getoptstr = "+ab:c:de:fghimnopqr:st:uv:xABC:LMNRSTUV::X12348";
+static char *getoptstr = "+ab:c:de:fghimnopqr:st:uv:xABC:GLMNRSTUV::X012348";
 
 static struct option long_options[] =
   {
@@ -1908,6 +1960,7 @@
     {"temp"            , required_argument, NULL, 0},
     {"single-threaded" ,       no_argument, NULL, 0},
     {"ignore-length"   ,       no_argument, NULL, 0},
+    {"norm"            ,       no_argument, NULL, 0},
 
     {"bits"            , required_argument, NULL, 'b'},
     {"channels"        , required_argument, NULL, 'c'},
@@ -1924,6 +1977,7 @@
     {"show-progress"   ,       no_argument, NULL, 'S'},
     {"type"            , required_argument, NULL, 't'},
     {"volume"          , required_argument, NULL, 'v'},
+    {"guard"           ,       no_argument, NULL, 'G'},
 
     {NULL, 0, NULL, 0}
   };
@@ -2056,57 +2110,25 @@
         sox_globals.input_bufsiz = i;
         break;
 
-      case 7:
-        interactive = sox_true;
-        break;
-
-      case 8:
-        usage_effect(optarg);
-        break;
-
-      case 9:
-        usage_format(optarg);
-        break;
-
-      case 10:
-        f->no_glob = sox_true;
-        break;
-
+      case 7: interactive = sox_true; break;
+      case 8: usage_effect(optarg); break;
+      case 9: usage_format(optarg); break;
+      case 10: f->no_glob = sox_true; break;
       case 11:
         sox_effects_globals.plot = enum_option(option_index, plot_methods);
         break;
-
-      case 12:
-        replay_gain_mode = enum_option(option_index, rg_modes);
-        break;
-
-      case 13:
-        display_SoX_version(stdout);
-        exit(0);
-        break;
-
-      case 14:
-        break;
-
-      case 15:
-        effects_filename = strdup(optarg);
-        break;
-
-      case 16:
-        sox_globals.tmp_path = strdup(optarg);
-        break;
-
-      case 17:
-        single_threaded = sox_true;
-        break;
-
-      case 18:
-        f->signal.length = SOX_IGNORE_LENGTH;
-        break;
-
+      case 12: replay_gain_mode = enum_option(option_index, rg_modes); break;
+      case 13: display_SoX_version(stdout); exit(0); break;
+      case 14: break;
+      case 15: effects_filename = strdup(optarg); break;
+      case 16: sox_globals.tmp_path = strdup(optarg); break;
+      case 17: single_threaded = sox_true; break;
+      case 18: f->signal.length = SOX_IGNORE_LENGTH; break;
+      case 19: do_guarded_norm = is_guarded = sox_true; break;
       }
       break;
 
+    case 'G': is_guarded = sox_true; break;
     case 'm': combine_method = sox_mix; break;
     case 'M': combine_method = sox_merge; break;
     case 'T': combine_method = sox_multiply; break;
@@ -2119,7 +2141,7 @@
       return c;
       break;
 
-    case 'h': 
+    case 'h':
       usage(NULL);
       break;
 
@@ -2144,6 +2166,8 @@
       break;
     }
 
+    case '0': f->encoding.half_bit = sox_true; break;
+
     case 'v':
       if (sscanf(optarg, "%lf %c", &f->volume, &dummy) != 1) {
         lsx_fail("Volume value `%s' is not a number", optarg);
@@ -2444,7 +2468,7 @@
         }
         sox_globals.verbosity = (unsigned)i;
       }
-    } 
+    }
     else if (opt == 'T')
       do_total = sox_true;
     else type = 1 + (strchr(opts, opt) - opts);
@@ -2512,12 +2536,6 @@
   return c1 && c2 && !strcasecmp(c1, c2);
 }
 
-static int strends(char const * str, char const * end)
-{
-  size_t str_len = strlen(str), end_len = strlen(end);
-  return str_len >= end_len && !strcmp(str + str_len - end_len, end);
-}
-
 #ifdef __CYGWIN__
 static char * check_dir(char * name)
 {
@@ -2534,11 +2552,11 @@
   myname = argv[0];
   sox_globals.output_message_handler = output_message;
 
-  if (strends(myname, "play"))
+  if (lsx_strends(myname, "play"))
     sox_mode = sox_play;
-  else if (strends(myname, "rec"))
+  else if (lsx_strends(myname, "rec"))
     sox_mode = sox_rec;
-  else if (strends(myname, "soxi"))
+  else if (lsx_strends(myname, "soxi"))
     sox_mode = sox_soxi;
 
   if (sox_init() != SOX_SUCCESS)
@@ -2686,7 +2704,7 @@
   }
 
   /* Not the best way for users to do this; now deprecated in favour of soxi. */
-  if (!show_progress && !nuser_effects[current_eff_chain] && 
+  if (!show_progress && !nuser_effects[current_eff_chain] &&
       ofile->filetype && !strcmp(ofile->filetype, "null")) {
     for (i = 0; i < input_count; i++)
       report_file_info(files[i]);
@@ -2703,7 +2721,7 @@
 
   /* If user specified an effects filename then use that file
    * to load user effects.  Free any previously specified options
-   * from the command line.  
+   * from the command line.
    */
   if (effects_filename)
   {
--- a/src/sox.h
+++ b/src/sox.h
@@ -1,6 +1,6 @@
 /* libSoX Library Public Interface
  *
- * Copyright 1999-2008 Chris Bagwell and SoX Contributors.
+ * Copyright 1999-2009 Chris Bagwell and SoX Contributors.
  *
  * This source code is freely redistributable and may be used for
  * any purpose.  This copyright notice must be maintained.
@@ -12,6 +12,7 @@
 #define SOX_H
 
 #include <limits.h>
+#include <math.h>
 #include <stdarg.h>
 #include <stddef.h> /* Ensure NULL etc. are available throughout SoX */
 #include <stdio.h>
@@ -75,40 +76,32 @@
 
 /*                Conversions: Linear PCM <--> sox_sample_t
  *
- *   I/O       I/O     sox_sample_t Clips?    I/O     sox_sample_t Clips?
- *  Format   Minimum     Minimum     I O    Maximum     Maximum     I O
- *  ------  ---------  ------------ -- --   --------  ------------ -- --
- *  Float      -1     -1.00000000047 y y       1           1        y n
- *  Int8      -128        -128       n n      127     127.9999999   n y
- *  Int16    -32768      -32768      n n     32767    32767.99998   n y
- *  Int24   -8388608    -8388608     n n    8388607   8388607.996   n y
- *  Int32  -2147483648 -2147483648   n n   2147483647 2147483647    n n
+ *   I/O       I/O     sox_sample_t    I/O     sox_sample_t
+ *  Format   Minimum     Minimum     Maximum     Maximum
+ *  ------  ---------  ------------  --------  ------------
+ *  Float      -1          -1           1       1 - 5e-10
+ *  Int8      -128        -127.5       127     127.4999999
+ *  Int16    -32768      -32767.5     32767    32767.49998
+ *  Int24   -8388608    -8388607.5   8388607   8388607.496
+ *  Int32  -2147483648 -2147483648  2147483647 2147483647
  *
  * Conversions are as accurate as possible (with rounding).
  *
  * Rounding: halves toward +inf, all others to nearest integer.
  *
- * Clips? shows whether on not there is the possibility of a conversion
- * clipping to the minimum or maximum value when inputing from or outputing
- * to a given type.
+ * The only possibility of clipping on conversion is when inputting Float.
  *
  * Unsigned integers are converted to and from signed integers by flipping
  * the upper-most bit then treating them as signed integers.
  */
 
-#define SOX_SAMPLE_LOCALS sox_sample_t sox_macro_temp_sample UNUSED; \
-  double sox_macro_temp_double UNUSED
+#define SOX_SAMPLE_LOCALS double sox_macro_temp_double UNUSED
 
 #define SOX_SAMPLE_NEG SOX_INT_MIN(32)
-#define SOX_SAMPLE_TO_UNSIGNED(bits,d,clips) \
-  (uint##bits##_t)( \
-    sox_macro_temp_sample=(d), \
-    sox_macro_temp_sample>(sox_sample_t)(SOX_SAMPLE_MAX-(1U<<(31-bits)))? \
-      ++(clips),SOX_UINT_MAX(bits): \
-      ((uint32_t)(sox_macro_temp_sample^SOX_SAMPLE_NEG)+(1U<<(31-bits)))>>(32-bits))
-#define SOX_SAMPLE_TO_SIGNED(bits,d,clips) \
-  (int##bits##_t)(SOX_SAMPLE_TO_UNSIGNED(bits,d,clips)^SOX_INT_MIN(bits))
-#define SOX_SIGNED_TO_SAMPLE(bits,d)((sox_sample_t)(d)<<(32-bits))
+#define SOX_SAMPLE_TO_UNSIGNED(bits,d) \
+  (uint##bits##_t)(((uint32_t)((d)^SOX_SAMPLE_NEG))>>(32-bits))
+#define SOX_SAMPLE_TO_SIGNED(bits,d) (int##bits##_t)(((uint32_t)(d))>>(32-bits))
+#define SOX_SIGNED_TO_SAMPLE(bits,d)(((sox_sample_t)(d)<<(32-bits))+(1<<(31-bits)))
 #define SOX_UNSIGNED_TO_SAMPLE(bits,d)(SOX_SIGNED_TO_SAMPLE(bits,d)^SOX_SAMPLE_NEG)
 
 #define SOX_UNSIGNED_8BIT_TO_SAMPLE(d,clips) SOX_UNSIGNED_TO_SAMPLE(8,d)
@@ -120,17 +113,17 @@
 #define SOX_UNSIGNED_32BIT_TO_SAMPLE(d,clips) ((sox_sample_t)(d)^SOX_SAMPLE_NEG)
 #define SOX_SIGNED_32BIT_TO_SAMPLE(d,clips) (sox_sample_t)(d)
 #define SOX_FLOAT_32BIT_TO_SAMPLE SOX_FLOAT_64BIT_TO_SAMPLE
-#define SOX_FLOAT_64BIT_TO_SAMPLE(d,clips) (sox_macro_temp_double=(d),sox_macro_temp_double<-1?++(clips),(-SOX_SAMPLE_MAX):sox_macro_temp_double>1?++(clips),SOX_SAMPLE_MAX:(sox_sample_t)((uint32_t)((double)(sox_macro_temp_double)*SOX_SAMPLE_MAX+(SOX_SAMPLE_MAX+.5))-SOX_SAMPLE_MAX))
-#define SOX_SAMPLE_TO_UNSIGNED_8BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(8,d,clips)
-#define SOX_SAMPLE_TO_SIGNED_8BIT(d,clips) SOX_SAMPLE_TO_SIGNED(8,d,clips)
-#define SOX_SAMPLE_TO_UNSIGNED_16BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(16,d,clips)
-#define SOX_SAMPLE_TO_SIGNED_16BIT(d,clips) SOX_SAMPLE_TO_SIGNED(16,d,clips)
-#define SOX_SAMPLE_TO_UNSIGNED_24BIT(d,clips) SOX_SAMPLE_TO_UNSIGNED(24,d,clips)
-#define SOX_SAMPLE_TO_SIGNED_24BIT(d,clips) SOX_SAMPLE_TO_SIGNED(24,d,clips)
-#define SOX_SAMPLE_TO_UNSIGNED_32BIT(d,clips) (uint32_t)((d)^SOX_SAMPLE_NEG)
-#define SOX_SAMPLE_TO_SIGNED_32BIT(d,clips) (int32_t)(d)
+#define SOX_FLOAT_64BIT_TO_SAMPLE(d,clips) (sox_macro_temp_double=(d)*(SOX_SAMPLE_MAX+1.)+.5,sox_macro_temp_double<SOX_SAMPLE_MIN?++(clips),SOX_SAMPLE_MIN:sox_macro_temp_double>=SOX_SAMPLE_MAX+1.?sox_macro_temp_double>SOX_SAMPLE_MAX+1.5?++(clips),SOX_SAMPLE_MAX:SOX_SAMPLE_MAX:(sox_sample_t)floor(sox_macro_temp_double))
+#define SOX_SAMPLE_TO_UNSIGNED_8BIT(d) SOX_SAMPLE_TO_UNSIGNED(8,d)
+#define SOX_SAMPLE_TO_SIGNED_8BIT(d) SOX_SAMPLE_TO_SIGNED(8,d)
+#define SOX_SAMPLE_TO_UNSIGNED_16BIT(d) SOX_SAMPLE_TO_UNSIGNED(16,d)
+#define SOX_SAMPLE_TO_SIGNED_16BIT(d) SOX_SAMPLE_TO_SIGNED(16,d)
+#define SOX_SAMPLE_TO_UNSIGNED_24BIT(d) SOX_SAMPLE_TO_UNSIGNED(24,d)
+#define SOX_SAMPLE_TO_SIGNED_24BIT(d) SOX_SAMPLE_TO_SIGNED(24,d)
+#define SOX_SAMPLE_TO_UNSIGNED_32BIT(d) (uint32_t)((d)^SOX_SAMPLE_NEG)
+#define SOX_SAMPLE_TO_SIGNED_32BIT(d) (int32_t)(d)
 #define SOX_SAMPLE_TO_FLOAT_32BIT SOX_SAMPLE_TO_FLOAT_64BIT
-#define SOX_SAMPLE_TO_FLOAT_64BIT(d,clips) (sox_macro_temp_sample=(d),sox_macro_temp_sample==SOX_SAMPLE_MIN?++(clips),-1.0:((double)(sox_macro_temp_sample)*(1.0/SOX_SAMPLE_MAX)))
+#define SOX_SAMPLE_TO_FLOAT_64BIT(d) ((d)*(1./(SOX_SAMPLE_MAX+1.)))
 
 
 
@@ -250,6 +243,7 @@
 typedef struct { /* Encoding parameters */
   sox_encoding_t encoding; /* format of sample numbers */
   unsigned bits_per_sample;  /* 0 if unknown or variable; uncompressed value if lossless; compressed value if lossy */
+  sox_bool half_bit;
   double compression;      /* compression factor (where applicable) */
 
   /* If these 3 variables are set to DEFAULT, then, during
@@ -452,6 +446,8 @@
 #define SOX_EFF_MCHAN    16          /* Effect can handle multi-channel */
 #define SOX_EFF_NULL     32          /* Effect does nothing */
 #define SOX_EFF_DEPRECATED 64        /* Effect is living on borrowed time */
+#define SOX_EFF_GAIN     128         /* Effect does not support gain -r */
+#define SOX_EFF_MODIFY   256         /* Effect does not modify samples */
 
 typedef enum {sox_plot_off, sox_plot_octave, sox_plot_gnuplot} sox_plot_t;
 typedef struct sox_effect sox_effect_t;
@@ -508,11 +504,11 @@
   sox_sample_t **ibufc, **obufc; /* Channel interleave buffers */
   sox_effects_globals_t global_info;
   sox_encodinginfo_t const * in_enc;
-  sox_encodinginfo_t const * out_enc;
+  sox_encodinginfo_t * out_enc;
 };
 typedef struct sox_effects_chain sox_effects_chain_t;
 sox_effects_chain_t * sox_create_effects_chain(
-    sox_encodinginfo_t const * in_enc, sox_encodinginfo_t const * out_enc);
+    sox_encodinginfo_t const * in_enc, sox_encodinginfo_t * out_enc);
 void sox_delete_effects_chain(sox_effects_chain_t *ecp);
 int sox_add_effect( sox_effects_chain_t * chain, sox_effect_t * effp, sox_signalinfo_t * in, sox_signalinfo_t const * out);
 int sox_flow_effects(sox_effects_chain_t *, int (* callback)(sox_bool all_done));
@@ -565,6 +561,7 @@
 lsx_enum_item const * lsx_find_enum_text(char const * text, lsx_enum_item const * lsx_enum_items);
 lsx_enum_item const * lsx_find_enum_value(unsigned value, lsx_enum_item const * lsx_enum_items);
 int lsx_enum_option(int c, lsx_enum_item const * items);
+sox_bool lsx_strends(char const * str, char const * end);
 char const * lsx_find_file_extension(char const * pathname);
 char const * lsx_sigfigs3(size_t number);
 char const * lsx_sigfigs3p(double percentage);
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -19,6 +19,17 @@
 
 #include <errno.h>
 
+#if defined(LSX_EFF_ALIAS)
+#undef lsx_debug
+#undef lsx_fail
+#undef lsx_report
+#undef lsx_warn
+#define lsx_debug sox_globals.subsystem=effp->handler.name,lsx_debug
+#define lsx_fail sox_globals.subsystem=effp->handler.name,lsx_fail
+#define lsx_report sox_globals.subsystem=effp->handler.name,lsx_report
+#define lsx_warn sox_globals.subsystem=effp->handler.name,lsx_warn
+#endif
+
 #define RANQD1 ranqd1(sox_globals.ranqd1)
 #define DRANQD1 dranqd1(sox_globals.ranqd1)
 
@@ -213,7 +224,6 @@
 int lsx_check_read_params(sox_format_t * ft, unsigned channels,
     sox_rate_t rate, sox_encoding_t encoding, unsigned bits_per_sample,
     off_t num_samples, sox_bool check_length);
-sox_sample_t lsx_sample_max(sox_encodinginfo_t const * encoding);
 #define SOX_FORMAT_HANDLER(name) \
 sox_format_handler_t const * sox_##name##_format_fn(void); \
 sox_format_handler_t const * sox_##name##_format_fn(void)
--- a/src/sox_sample_test.c
+++ /dev/null
@@ -1,18 +1,0 @@
-/* libSoX test code    copyright (c) 2006 robs@users.sourceforge.net
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#include "sox_sample_test.h"
--- a/src/sox_sample_test.h
+++ /dev/null
@@ -1,207 +1,0 @@
-/* libSoX test code    copyright (c) 2006 robs@users.sourceforge.net
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the
- * Free Software Foundation; either version 2 of the License, or (at your
- * option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
- * Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-#define DEBUG
-#include <assert.h>
-#include "sox.h"
-
-#define TEST_UINT(bits) \
-  uint##bits = 0; \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,uint##bits); \
-  assert(sample == SOX_SAMPLE_MIN); \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == 0 && clips == 0); \
- \
-  uint##bits = 1; \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,uint##bits); \
-  assert(sample > SOX_SAMPLE_MIN && sample < 0); \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == 1 && clips == 0); \
- \
-  uint##bits = SOX_INT_MAX(bits); \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,uint##bits); \
-  assert(sample * SOX_INT_MAX(bits) == SOX_UNSIGNED_TO_SAMPLE(bits,1)); \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == SOX_INT_MAX(bits) && clips == 0); \
- \
-  sample =SOX_UNSIGNED_TO_SAMPLE(bits,1)+SOX_UNSIGNED_TO_SAMPLE(bits,SOX_INT_MAX(bits))/2; \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == 1 && clips == 0); \
- \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,1)+SOX_UNSIGNED_TO_SAMPLE(bits,SOX_INT_MAX(bits))/2-1; \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == 0 && clips == 0); \
- \
-  uint##bits = (0^SOX_INT_MIN(bits)); \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,uint##bits); \
-  assert(sample == 0); \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == (0^SOX_INT_MIN(bits)) && clips == 0); \
- \
-  uint##bits = ((0^SOX_INT_MIN(bits))+1); \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,uint##bits); \
-  assert(sample > 0 && sample < SOX_SAMPLE_MAX); \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == ((0^SOX_INT_MIN(bits))+1) && clips == 0); \
- \
-  uint##bits = SOX_UINT_MAX(bits); \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,uint##bits); \
-  assert(sample == SOX_INT_MAX(bits) * SOX_UNSIGNED_TO_SAMPLE(bits,((0^SOX_INT_MIN(bits))+1))); \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == SOX_UINT_MAX(bits) && clips == 0); \
- \
-  sample =SOX_UNSIGNED_TO_SAMPLE(bits,SOX_UINT_MAX(bits))+SOX_UNSIGNED_TO_SAMPLE(bits,((0^SOX_INT_MIN(bits))+1))/2-1; \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == SOX_UINT_MAX(bits) && clips == 0); \
- \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,SOX_UINT_MAX(bits))+SOX_UNSIGNED_TO_SAMPLE(bits,((0^SOX_INT_MIN(bits))+1))/2; \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == SOX_UINT_MAX(bits) && --clips == 0); \
- \
-  sample = SOX_SAMPLE_MAX; \
-  uint##bits = SOX_SAMPLE_TO_UNSIGNED(bits,sample, clips); \
-  assert(uint##bits == SOX_UINT_MAX(bits) && --clips == 0); \
-
-#define TEST_SINT(bits) \
-  int##bits = SOX_INT_MIN(bits); \
-  sample = SOX_SIGNED_TO_SAMPLE(bits,int##bits); \
-  assert(sample == SOX_SAMPLE_MIN); \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  int##bits = SOX_INT_MIN(bits)+1; \
-  sample = SOX_SIGNED_TO_SAMPLE(bits,int##bits); \
-  assert(sample > SOX_SAMPLE_MIN && sample < 0); \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  int##bits = SOX_UINT_MAX(bits) /* i.e. -1 */; \
-  sample = SOX_SIGNED_TO_SAMPLE(bits,int##bits); \
-  assert(sample * SOX_INT_MAX(bits) == SOX_SIGNED_TO_SAMPLE(bits,SOX_INT_MIN(bits)+1)); \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  int##bits = SOX_INT_MIN(bits)+1; \
-  sample =SOX_UNSIGNED_TO_SAMPLE(bits,1)+SOX_UNSIGNED_TO_SAMPLE(bits,SOX_INT_MAX(bits))/2; \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  int##bits = SOX_INT_MIN(bits); \
-  sample = SOX_UNSIGNED_TO_SAMPLE(bits,1)+SOX_UNSIGNED_TO_SAMPLE(bits,SOX_INT_MAX(bits))/2-1; \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  int##bits = 0; \
-  sample = SOX_SIGNED_TO_SAMPLE(bits,int##bits); \
-  assert(sample == 0); \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  int##bits = 1; \
-  sample = SOX_SIGNED_TO_SAMPLE(bits,int##bits); \
-  assert(sample > 0 && sample < SOX_SAMPLE_MAX); \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  int##bits = SOX_INT_MAX(bits); \
-  sample = SOX_SIGNED_TO_SAMPLE(bits,int##bits); \
-  assert(sample == SOX_INT_MAX(bits) * SOX_SIGNED_TO_SAMPLE(bits,1)); \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  sample =SOX_UNSIGNED_TO_SAMPLE(bits,SOX_UINT_MAX(bits))+SOX_UNSIGNED_TO_SAMPLE(bits,((0^SOX_INT_MIN(bits))+1))/2-1; \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && clips == 0); \
- \
-  sample =SOX_UNSIGNED_TO_SAMPLE(bits,SOX_UINT_MAX(bits))+SOX_UNSIGNED_TO_SAMPLE(bits,((0^SOX_INT_MIN(bits))+1))/2; \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && --clips == 0); \
- \
-  sample = SOX_SAMPLE_MAX; \
-  int##bits##_2 = SOX_SAMPLE_TO_SIGNED(bits,sample, clips); \
-  assert(int##bits##_2 == int##bits && --clips == 0);
-
-#if defined __GNUC__
-  #pragma GCC system_header
-#elif defined __SUNPRO_CC
-  #pragma disable_warn
-#elif defined _MSC_VER
-  #pragma warning(push, 1)
-#endif
-
-#define epsilon 1e-16
-
-int main()
-{
-  int8_t int8;
-  int16_t int16;
-  int24_t int24;
-
-  uint8_t uint8;
-  uint16_t uint16;
-  uint24_t uint24;
-
-  int8_t int8_2;
-  int16_t int16_2;
-  int24_t int24_2;
-
-  sox_sample_t sample;
-  size_t clips = 0;
-
-  double d;
-  SOX_SAMPLE_LOCALS;
-
-  TEST_UINT(8)
-  TEST_UINT(16)
-  TEST_UINT(24)
-
-  TEST_SINT(8)
-  TEST_SINT(16)
-  TEST_SINT(24)
-
-  d = -1.0000000001;
-  sample = SOX_FLOAT_64BIT_TO_SAMPLE(d, clips);
-  assert(sample == -SOX_SAMPLE_MAX && --clips == 0);
-
-  d = -1;
-  sample = SOX_FLOAT_64BIT_TO_SAMPLE(d, clips);
-  assert(sample == -SOX_SAMPLE_MAX && clips == 0);
-  d = SOX_SAMPLE_TO_FLOAT_64BIT(sample,clips);
-  assert(fabs(d + 1) < epsilon && clips == 0);
-
-  --sample;
-  d = SOX_SAMPLE_TO_FLOAT_64BIT(sample,clips);
-  assert(fabs(d + 1) < epsilon && --clips == 0);
-
-  d = 1;
-  sample = SOX_FLOAT_64BIT_TO_SAMPLE(d, clips);
-  assert(sample == SOX_SAMPLE_MAX && clips == 0);
-  d = SOX_SAMPLE_TO_FLOAT_64BIT(sample,clips);
-  assert(fabs(d - 1) < epsilon && clips == 0);
-
-  d = 1.0000000001;
-  sample = SOX_FLOAT_64BIT_TO_SAMPLE(d, clips);
-  assert(sample == SOX_SAMPLE_MAX && --clips == 0);
-
-  return 0;
-}
-
-#if defined __SUNPRO_CC
-  #pragma enable_warn
-#elif defined _MSC_VER
-  #pragma warning(pop)
-#endif
--- a/src/spectrogram.c
+++ b/src/spectrogram.c
@@ -186,7 +186,7 @@
     size_t * isamp, size_t * osamp)
 {
   priv_t * p = (priv_t *)effp->priv;
-  size_t len = min(*isamp, *osamp), dummy = 0; /* No need to clip count */
+  size_t len = min(*isamp, *osamp);
   int i;
 
   memcpy(obuf, ibuf, len * sizeof(*obuf)); /* Pass on audio unaffected */
@@ -193,7 +193,6 @@
   *isamp = *osamp = len;
 
   while (sox_true) {
-    SOX_SAMPLE_LOCALS;
     if (p->read == p->step_size) {
       memmove(p->buf, p->buf + p->step_size,
           (p->dft_size - p->step_size) * sizeof(*p->buf));
@@ -200,7 +199,7 @@
       p->read = 0;
     }
     for (; len && p->read < p->step_size; --len, ++p->read, --p->end)
-      p->buf[p->dft_size - p->step_size + p->read] = FROM_SOX(*ibuf++, dummy);
+      p->buf[p->dft_size - p->step_size + p->read] = FROM_SOX(*ibuf++);
     if (p->read != p->step_size)
       break;
 
@@ -494,7 +493,7 @@
 sox_effect_handler_t const * sox_spectrogram_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "spectrogram", 0, 0, getopts, start, flow, drain, stop, 0, sizeof(priv_t)};
+    "spectrogram", 0, SOX_EFF_MODIFY, getopts, start, flow, drain, stop, 0, sizeof(priv_t)};
   static char const * lines[] = {
     "[options]",
     "\t-x num\tX-axis pixels/second, default 100",
--- a/src/splice.c
+++ b/src/splice.c
@@ -185,7 +185,7 @@
           break;
         }
         for (c = 0; c < effp->in_signal.channels; ++c)
-          p->buffer[p->buffer_pos++] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++, effp->clips);
+          p->buffer[p->buffer_pos++] = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++);
       }
       break;
     }
--- a/src/stat.c
+++ b/src/stat.c
@@ -141,8 +141,7 @@
 
     if (stat->fft) {
       for (x = 0; x < len; x++) {
-        SOX_SAMPLE_LOCALS;
-        stat->re_in[stat->fft_offset++] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[x], effp->clips);
+        stat->re_in[stat->fft_offset++] = SOX_SAMPLE_TO_FLOAT_32BIT(ibuf[x]);
 
         if (stat->fft_offset >= stat->fft_size) {
           stat->fft_offset = 0;
@@ -321,7 +320,7 @@
 static sox_effect_handler_t sox_stat_effect = {
   "stat",
   "[ -s N ] [ -rms ] [-freq] [ -v ] [ -d ]",
-  SOX_EFF_MCHAN,
+  SOX_EFF_MCHAN | SOX_EFF_MODIFY,
   sox_stat_getopts,
   sox_stat_start,
   sox_stat_flow,
--- a/src/synth.c
+++ b/src/synth.c
@@ -168,7 +168,6 @@
   char *        length_str;
   channel_t *      getopts_channels;
   size_t    getopts_nchannels;
-  sox_sample_t  max;
   size_t    samples_done;
   size_t    samples_to_do;
   channel_t *      channels;
@@ -378,7 +377,6 @@
   priv_t * synth = (priv_t *) effp->priv;
   size_t i, j;
 
-  synth->max = lsx_sample_max(effp->out_encoding);
   synth->samples_done = 0;
 
   if (synth->length_str)
@@ -438,9 +436,6 @@
   return SOX_SUCCESS;
 }
 
-
-
-#define sign(d) ((d) < 0? -1. : 1.)
 #define elapsed_time_s synth->samples_done / effp->in_signal.rate
 
 static int flow(sox_effect_t * effp, const sox_sample_t * ibuf, sox_sample_t * obuf,
@@ -607,11 +602,12 @@
       synth_out = synth_out * (1 - fabs(chan->offset)) + chan->offset;
 
       switch (chan->combine) {
-        case synth_create: *obuf++ =  synth_out * synth->max; break;
-        case synth_mix   : *obuf++ = (synth_out * synth->max + synth_input) * 0.5; break;
-        case synth_amod  : *obuf++ = (synth_out + 1) * synth_input * 0.5; break;
-        case synth_fmod  : *obuf++ =  synth_out * synth_input; break;
+        case synth_create: synth_out *=  SOX_SAMPLE_MAX; break;
+        case synth_mix   : synth_out = (synth_out * SOX_SAMPLE_MAX + synth_input) * 0.5; break;
+        case synth_amod  : synth_out = (synth_out + 1) * synth_input * 0.5; break;
+        case synth_fmod  : synth_out *=  synth_input; break;
       }
+      *obuf++ = floor(synth_out + .5);
     }
     if (++synth->samples_done == synth->samples_to_do)
       result = SOX_EOF;
@@ -649,7 +645,7 @@
 {
   static sox_effect_handler_t handler = {
     "synth", "[len] {type [combine] [[%]freq[k][:|+|/|-[%]freq2[k]] [off [ph [p1 [p2 [p3]]]]]]}",
-    SOX_EFF_MCHAN | SOX_EFF_PREC |SOX_EFF_LENGTH,
+    SOX_EFF_MCHAN | SOX_EFF_PREC |SOX_EFF_LENGTH | SOX_EFF_GAIN,
     getopts, start, flow, 0, stop, kill, sizeof(priv_t)
   };
   return &handler;
--- a/src/tempo.c
+++ b/src/tempo.c
@@ -272,7 +272,7 @@
   if (*isamp && odone < *osamp) {
     float * t = tempo_input(p->tempo, NULL, *isamp / effp->in_signal.channels);
     for (i = *isamp; i; --i)
-      *t++ = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++, effp->clips);
+      *t++ = SOX_SAMPLE_TO_FLOAT_32BIT(*ibuf++);
     tempo_process(p->tempo);
   }
   else *isamp = 0;
--- a/src/tests.sh
+++ b/src/tests.sh
@@ -200,8 +200,6 @@
 
 # Run tests
 
-${builddir}/sox_sample_test || exit 1
-
 skip_check caf flac mat4 mat5 paf w64 wv
 
 vectors=0
--- a/src/trim.c
+++ b/src/trim.c
@@ -172,7 +172,7 @@
 const sox_effect_handler_t *sox_trim_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "trim", "start [length]", SOX_EFF_MCHAN|SOX_EFF_LENGTH,
+    "trim", "start [length]", SOX_EFF_MCHAN | SOX_EFF_LENGTH | SOX_EFF_MODIFY,
     sox_trim_getopts, sox_trim_start, sox_trim_flow,
     NULL, NULL, kill, sizeof(priv_t)
   };
--- a/src/util.c
+++ b/src/util.c
@@ -37,6 +37,12 @@
 }
 #endif
 
+sox_bool lsx_strends(char const * str, char const * end)
+{
+  size_t str_len = strlen(str), end_len = strlen(end);
+  return str_len >= end_len && !strcmp(str + str_len - end_len, end);
+}
+
 char const * lsx_find_file_extension(char const * pathname)
 {
   /* First, chop off any path portions of filename.  This
--- a/src/util.h
+++ b/src/util.h
@@ -31,14 +31,22 @@
 
 #ifdef _MSC_VER
 #define __STDC__ 1
-#define S_IFMT   _S_IFMT
-#define S_IFREG  _S_IFREG
 #define O_BINARY _O_BINARY
+#define O_CREAT _O_CREAT
+#define O_RDWR _O_RDWR
+#define O_TRUNC _O_TRUNC
+#define S_IFMT _S_IFMT
+#define S_IFREG _S_IFREG
+#define S_IREAD _S_IREAD
+#define S_IWRITE _S_IWRITE
+#define fdopen _fdopen
 #define fstat _fstat
 #define ftime _ftime
 #define inline __inline
 #define isatty _isatty
+#define mktemp _mktemp
 #define off_t _off_t
+#define open _open
 #define popen _popen
 #define stat _stat
 #define strdup _strdup
@@ -91,6 +99,8 @@
 #ifndef M_SQRT2
 #define M_SQRT2  sqrt(2.)
 #endif
+
+#define sign(x) ((x) < 0? -1 : 1)
 
 /* Numerical Recipes in C, p. 284 */
 #define ranqd1(x) ((x) = 1664525L * (x) + 1013904223L) /* int32_t x */
binary files a/src/vectors/intermediate1_44100_-u_-1_wav_-N.s1 b/src/vectors/intermediate1_44100_-u_-1_wav_-N.s1 differ
binary files a/src/vectors/intermediate1_44100_-u_-1_wav_-X.s1 b/src/vectors/intermediate1_44100_-u_-1_wav_-X.s1 differ
binary files a/src/vectors/intermediate1_44100_-u_-1_wav_-X_-N.s1 b/src/vectors/intermediate1_44100_-u_-1_wav_-X_-N.s1 differ
binary files a/src/vectors/intermediate1_44100_-u_-1_wav_.s1 b/src/vectors/intermediate1_44100_-u_-1_wav_.s1 differ
--- a/src/voc.c
+++ b/src/voc.c
@@ -347,7 +347,6 @@
       /* Read the data in the file */
       if (v->size <= 4) {
         if (!v->adpcm.setup.sign) {
-          SOX_SAMPLE_LOCALS;
           if (lsx_readb(ft, &uc) == SOX_EOF) {
             lsx_warn("VOC input: short file");
             v->block_remaining = 0;
@@ -354,7 +353,7 @@
             return done;
           }
           *buf = SOX_UNSIGNED_8BIT_TO_SAMPLE(uc,);
-          lsx_adpcm_init(&v->adpcm, 6 - v->size, SOX_SAMPLE_TO_SIGNED_16BIT(*buf, ft->clips));
+          lsx_adpcm_init(&v->adpcm, 6 - v->size, SOX_SAMPLE_TO_SIGNED_16BIT(*buf));
           ++buf;
           --v->block_remaining;
           ++done;
@@ -490,12 +489,11 @@
   }
   v->samples += len;
   while (done < len) {
-    SOX_SAMPLE_LOCALS;
     if (ft->encoding.bits_per_sample == 8) {
-      uc = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips);
+      uc = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++);
       lsx_writeb(ft, uc);
     } else {
-      sw = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
+      sw = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++);
       lsx_writesw(ft, sw);
     }
     done++;
--- a/src/vol.c
+++ b/src/vol.c
@@ -178,28 +178,7 @@
 sox_effect_handler_t const * sox_vol_effect_fn(void)
 {
   static sox_effect_handler_t handler = {
-    "vol", vol_usage, SOX_EFF_MCHAN, getopts, start, flow, 0, stop, 0, sizeof(priv_t)
+    "vol", vol_usage, SOX_EFF_MCHAN | SOX_EFF_GAIN, getopts, start, flow, 0, stop, 0, sizeof(priv_t)
   };
-  return &handler;
-}
-
-static int gain_getopts(sox_effect_t * effp, int argc, char * * argv)
-{
-  char * args[] = {0, 0, "dB"};
-
-  if (argc != 2)
-    return lsx_usage(effp);
-  args[0] = argv[0];
-  args[1] = argv[1];
-  return sox_vol_effect_fn()->getopts(effp, (int)array_length(args), args);
-}
-
-sox_effect_handler_t const * sox_gain_effect_fn(void)
-{
-  static sox_effect_handler_t handler;
-  handler = *sox_vol_effect_fn();
-  handler.name = "gain";
-  handler.usage = "dB-gain";
-  handler.getopts = gain_getopts;
   return &handler;
 }
--- a/src/wav.c
+++ b/src/wav.c
@@ -331,10 +331,9 @@
     ft->sox_errno = SOX_SUCCESS;
 
     while (done < len) {
-        SOX_SAMPLE_LOCALS;
         while ((wav->gsmindex < 160*2) && (done < len))
             wav->gsmsample[(wav->gsmindex)++] =
-                SOX_SAMPLE_TO_SIGNED_16BIT(buf[done++], ft->clips);
+                SOX_SAMPLE_TO_SIGNED_16BIT(buf[done++]);
 
         if (wav->gsmindex < 160*2)
             break;
--- a/src/wavpack.c
+++ b/src/wavpack.c
@@ -120,14 +120,13 @@
   int result;
 
   for (i = 0; i < len; ++i) switch (ft->encoding.bits_per_sample) {
-    SOX_SAMPLE_LOCALS;
-    case  8: obuf[i] = SOX_SAMPLE_TO_SIGNED_8BIT(buf[i], ft->clips); break;
-    case 16: obuf[i] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[i], ft->clips); break;
-    case 24: obuf[i] = SOX_SAMPLE_TO_SIGNED_24BIT(buf[i], ft->clips) << 8;
+    case  8: obuf[i] = SOX_SAMPLE_TO_SIGNED_8BIT(buf[i]); break;
+    case 16: obuf[i] = SOX_SAMPLE_TO_SIGNED_16BIT(buf[i]); break;
+    case 24: obuf[i] = SOX_SAMPLE_TO_SIGNED_24BIT(buf[i]) << 8;
              obuf[i] >>= 8; break;
     case 32: obuf[i] = ft->encoding.encoding == SOX_ENCODING_WAVPACKF?
-      SOX_SAMPLE_TO_SIGNED_24BIT(*(float *)&buf[i], ft->clips) :
-      SOX_SAMPLE_TO_SIGNED_32BIT(buf[i], ft->clips);
+      SOX_SAMPLE_TO_SIGNED_24BIT(*(float *)&buf[i]) :
+      SOX_SAMPLE_TO_SIGNED_32BIT(buf[i]);
       break;
   }
   result = WavpackPackSamples(p->codec, obuf, (uint32_t) len / ft->signal.channels);