shithub: sox

Download patch

ref: 40ad2f19bc2ec47e7b3b1e08a9903e82e8624954
parent: 280fbd36331c6e0b6607fc72ced19b16f8a1b1f2
author: robs <robs>
date: Sun Mar 16 03:52:57 EDT 2008

Can now write Sounder files
Can now write DEC-variant au files (with -x)
Comments support for SoundTool files
Fix IRCAM SF header processing; support all (modern) variants
Fix 24-bit read/write on big-endian systems
For some file-types, warn if file size seems too short
Added auto-detect for caf, sndr, txw & sf files
Fix endian selection (-B, -L, -x) in some circumstances
Reimplement (separately) SoundTool & Sounder format handlers

--- a/ChangeLog
+++ b/ChangeLog
@@ -26,12 +26,6 @@
 
   o New option --help-format shows info about supported format(s).  (robs)
   o New htk format.  (robs)
-  o Fix [1864216] comments mangled when writing ogg-vorbis.  (robs)
-  o Fix short noise at end of alsa playback.  (Morita Sho/Tim Munro/robs)
-  o Fix wve seek accuracy.  (robs)
-  o Fix lpc10 not working.  (robs)
-  o Fix [1187257] wav MS-ADPCM block-align size incorrect.  (robs)
-  o For wav & au, fix [548256] size in header wrong when piping out.  (robs)
   o Writing aiff, aifc & dvms now repeatable with -R.  (robs)
   o Writing hcom no longer fails with unsupported rate--chooses
     best match.  (robs)
@@ -40,6 +34,17 @@
   o Can now write .amb (.wav variant) files [FR 1902232].  (robs)
   o Can now read 2,3(2.6),4 bit ADPCM .voc files [FR 1714991].  (robs)
   o Can now read some MP3 ID3 tags.  (robs)
+  o Can now write Sounder files.  (robs)
+  o Can now write DEC-variant au files (with -x).  (robs)
+  o Comments support for SoundTool files.  (robs)
+  o Fix [1864216] comments mangled when writing ogg-vorbis.  (robs)
+  o Fix short noise at end of alsa playback.  (Morita Sho/Tim Munro/robs)
+  o Fix wve seek accuracy.  (robs)
+  o Fix lpc10 not working.  (robs)
+  o Fix [1187257] wav MS-ADPCM block-align size incorrect.  (robs)
+  o For wav & au, fix [548256] size in header wrong when piping out.  (robs)
+  o Fix IRCAM SF header processing; support all (modern) variants.  (robs)
+  o Fix 24-bit read/write on big-endian systems.  (robs)
 
 Effects:
 
@@ -69,6 +74,8 @@
     and default audio device (all).  (robs)
   o Simpler file info display for `play'.  (robs)
   o New `multiply' method for the input file combiner.  (robs)
+  o For some file-types, warn if file size seems too short.  (robs)
+  o Added auto-detect for caf, sndr, txw & sf files.  (robs)
 
 Other bug fixes:
 
@@ -75,14 +82,15 @@
   o Fix [1890983] rec shortcut should apply bit depth (8-bit,
     16-bit, etc.) to input handler.  (robs)
   o Fix auto-detect of hcom files.  (robs)
-  o Added auto-detect for caf, txw & sf files.  (robs)
   o Fix ungraceful handling of out of disc space and other write
     errors (bug was introduced in 14.0.0).  (robs)
+  o Fix endian selection (-B, -L, -x) in some circumstances.  (robs)
 
 Internal improvements:
 
   o Use FORTIFY_SOURCE with gcc.  (robs)
   o Fixed all compiler warnings (AFAICT).  (robs)
+  o Reimplement (separately) SoundTool & Sounder format handlers.  (robs)
 
 Deprecated features to be removed in future:
 
--- a/src/8svx.c
+++ b/src/8svx.c
@@ -341,7 +341,7 @@
         sox_writedw(ft, nsamples/ft->signal.channels);  /* samples, 1-shot */
         sox_writedw(ft, 0);  /* samples, repeat */
         sox_writedw(ft, 0);  /* samples per repeat cycle */
-        sox_writew(ft, (uint16_t)ft->signal.rate); /* samples per second */
+        sox_writew(ft, min(65535, (unsigned)(ft->signal.rate + .5)));
         sox_writeb(ft,1); /* number of octabes */
         sox_writeb(ft,0); /* data compression (none) */
         sox_writew(ft,1); sox_writew(ft,0); /* volume */
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -30,17 +30,17 @@
   dither          key             pitch           speed           vol
 )
 set(formats_srcs
-  8svx            cvsd-fmt        htk             s1-fmt          u2-fmt
-  adpcm           dat             ima-fmt         s2-fmt          u3-fmt
-  adpcms          dvms-fmt        ima_rw          s3-fmt          u4-fmt
-  aifc-fmt        formats         la-fmt          s4-fmt          ul-fmt
-  aiff            g711            lpc10.c         sf              voc
-  aiff-fmt        g721            lu-fmt          skelform        vox
-  al-fmt          g723_24         maud            smp             vox-fmt
-  au              g723_40         nulfile         sndrtool        wav
-  avr             g72x            prc             sphere          wve
-  cdr             gsm.c           raw             tx16w           xa
-  cvsd            hcom            raw-fmt         u1-fmt
+  8svx            cvsd-fmt        htk             s1-fmt          u1-fmt
+  adpcm           dat             ima-fmt         s2-fmt          u2-fmt
+  adpcms          dvms-fmt        ima_rw          s3-fmt          u3-fmt
+  aifc-fmt        formats         la-fmt          s4-fmt          u4-fmt
+  aiff            g711            lpc10.c         sf              ul-fmt
+  aiff-fmt        g721            lu-fmt          skelform        voc
+  al-fmt          g723_24         maud            smp             vox
+  au              g723_40         nulfile         sounder         vox-fmt
+  avr             g72x            prc             soundtool       wav
+  cdr             gsm.c           raw             sphere          wve
+  cvsd            hcom            raw-fmt         tx16w           xa
 )
 add_library(lib${PROJECT_NAME}
   ${effects_srcs}         misc                    util
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -40,11 +40,11 @@
 	  libsox_fmt_cdr.la libsox_fmt_cvsd.la libsox_fmt_dvms.la	\
 	  libsox_fmt_dat.la libsox_fmt_gsm.la libsox_fmt_hcom.la	\
 	  libsox_fmt_lpc10.la libsox_fmt_maud.la libsox_fmt_prc.la	\
-	  libsox_fmt_sf.la libsox_fmt_smp.la libsox_fmt_sndrtool.la	\
+	  libsox_fmt_sf.la libsox_fmt_smp.la	\
 	  libsox_fmt_sphere.la libsox_fmt_txw.la libsox_fmt_voc.la	\
 	  libsox_fmt_vox.la libsox_fmt_ima.la libsox_fmt_wav.la		\
 	  libsox_fmt_wve.la libsox_fmt_xa.la libsox_fmt_nul.la \
-	  libsox_fmt_htk.la
+	  libsox_fmt_htk.la libsox_fmt_sounder.la libsox_fmt_soundtool.la
 
 # File formats
 libsox_fmt_raw_la_SOURCES = raw-fmt.c
@@ -103,12 +103,14 @@
 libsox_fmt_maud_la_LIBADD = libsox.la
 libsox_fmt_prc_la_SOURCES = prc.c
 libsox_fmt_prc_la_LIBADD = libsox.la
-libsox_fmt_sf_la_SOURCES = sf.c sfircam.h
+libsox_fmt_sf_la_SOURCES = sf.c
 libsox_fmt_sf_la_LIBADD = libsox.la
 libsox_fmt_smp_la_SOURCES = smp.c
 libsox_fmt_smp_la_LIBADD = libsox.la
-libsox_fmt_sndrtool_la_SOURCES = sndrtool.c
-libsox_fmt_sndrtool_la_LIBADD = libsox.la
+libsox_fmt_sounder_la_SOURCES = sounder.c
+libsox_fmt_sounder_la_LIBADD = libsox.la
+libsox_fmt_soundtool_la_SOURCES = soundtool.c
+libsox_fmt_soundtool_la_LIBADD = libsox.la
 libsox_fmt_sphere_la_SOURCES = sphere.c
 libsox_fmt_sphere_la_LIBADD = libsox.la
 libsox_fmt_txw_la_SOURCES = tx16w.c
@@ -218,8 +220,8 @@
   libsox_la_SOURCES += raw-fmt.c s1-fmt.c s2-fmt.c s3-fmt.c \
     s4-fmt.c u1-fmt.c u2-fmt.c u3-fmt.c u4-fmt.c al-fmt.c la-fmt.c ul-fmt.c \
     lu-fmt.c 8svx.c aiff-fmt.c aifc-fmt.c au.c avr.c cdr.c cvsd-fmt.c \
-    dvms-fmt.c dat.c gsm.c hcom.c htk.c lpc10.c maud.c prc.c sf.c sfircam.h smp.c \
-    sndrtool.c sphere.c tx16w.c voc.c vox-fmt.c ima-fmt.c adpcm.c adpcm.h \
+    dvms-fmt.c dat.c gsm.c hcom.c htk.c lpc10.c maud.c prc.c sf.c smp.c \
+    sounder.c soundtool.c sphere.c tx16w.c voc.c vox-fmt.c ima-fmt.c adpcm.c adpcm.h \
     ima_rw.c ima_rw.h wav.c wav.h wve.c xa.c nulfile.c
 libsox_la_LIBADD = @GSM_LIBS@ @LIBGSM_LIBADD@
 libsox_la_LIBADD += ../lpc10/liblpc10.la
--- a/src/au.c
+++ b/src/au.c
@@ -14,7 +14,6 @@
  * We support only the common formats, plus
  * CCITT G.721 (32 kbit/s) and G.723 (24/40 kbit/s),
  * courtesy of Sun's public domain implementation.
- * Output is always in big-endian (Sun/NeXT) order.
  */
 
 #include "sox_i.h"
@@ -22,11 +21,14 @@
 #include <string.h>
 
 /* Magic numbers used in Sun and NeXT audio files */
-#define SUN_MAGIC     0x2e736e64  /* Really '.snd' */
-#define SUN_INV_MAGIC 0x646e732e  /* '.snd' reversed bytes */
-#define DEC_MAGIC     0x2e736400  /* Really '\0ds.' (for DEC) */
-#define DEC_INV_MAGIC 0x0064732e  /* '\0ds.' reversed bytes */
-#define SUN_HDRSIZE   24          /* Size of minimal header */
+static struct {char str[4]; sox_bool reverse_bytes; char const * desc;} id[] = {
+  {"\x2e\x73\x6e\x64", SOX_IS_LITTLEENDIAN, "Big-endian .snd"},
+  {"\x64\x6e\x73\x2e", SOX_IS_BIGENDIAN   , "Little-endian .snd"},
+  {"\x00\x64\x73\x2e", SOX_IS_BIGENDIAN   , "Little-endian '\0ds.' (for DEC)"},
+  {"\x2e\x73\x64\x00", SOX_IS_LITTLEENDIAN, "Big-endian '\0ds.'"},
+  {"    ", 0, NULL}
+};
+#define FIXED_HDR     24
 #define SUN_UNSPEC    ~0u         /* Unspecified data size (this is legal) */
 
 typedef enum {
@@ -34,7 +36,7 @@
   Double, Indirect, Nested, Dsp_core, Dsp_data_8, Dsp_data_16, Dsp_data_24,
   Dsp_data_32, Unknown, Display, Mulaw_squelch, Emphasized, Compressed,
   Compressed_emphasized, Dsp_commands, Dsp_commands_samples, Adpcm_g721,
-  Adpcm_g722, Adpcm_g723_3, Adpcm_g723_5, Alaw_8, Unknown_other} sun_encoding_t;
+  Adpcm_g722, Adpcm_g723_3, Adpcm_g723_5, Alaw_8, Unknown_other} ft_encoding_t;
 static char const * const str[] = {
   "Unspecified", "8-bit mu-law", "8-bit signed linear", "16-bit signed linear",
   "24-bit signed linear", "32-bit signed linear", "Floating-point",
@@ -46,40 +48,35 @@
   "Music Kit DSP samples", "4-bit G.721 ADPCM", "G.722 ADPCM",
   "3-bit G.723 ADPCM", "5-bit G.723 ADPCM", "8-bit a-law", "Unknown"};
 
-static sun_encoding_t sun_enc(unsigned size, sox_encoding_t sox_enc)
+static ft_encoding_t ft_enc(unsigned size, sox_encoding_t encoding)
 {
-  sun_encoding_t result = Unspecified;
-  if      (sox_enc == SOX_ENCODING_ULAW  && size ==  8) result = Mulaw_8;
-  else if (sox_enc == SOX_ENCODING_ALAW  && size ==  8) result = Alaw_8;
-  else if (sox_enc == SOX_ENCODING_SIGN2 && size ==  8) result = Linear_8;
-  else if (sox_enc == SOX_ENCODING_SIGN2 && size == 16) result = Linear_16;
-  else if (sox_enc == SOX_ENCODING_SIGN2 && size == 24) result = Linear_24;
-  else if (sox_enc == SOX_ENCODING_SIGN2 && size == 32) result = Linear_32;
-  else if (sox_enc == SOX_ENCODING_FLOAT && size == 32) result = Float;
-  else if (sox_enc == SOX_ENCODING_FLOAT && size == 64) result = Double;
-  return result;
+  if (encoding == SOX_ENCODING_ULAW  && size ==  8) return Mulaw_8;
+  if (encoding == SOX_ENCODING_ALAW  && size ==  8) return Alaw_8;
+  if (encoding == SOX_ENCODING_SIGN2 && size ==  8) return Linear_8;
+  if (encoding == SOX_ENCODING_SIGN2 && size == 16) return Linear_16;
+  if (encoding == SOX_ENCODING_SIGN2 && size == 24) return Linear_24;
+  if (encoding == SOX_ENCODING_SIGN2 && size == 32) return Linear_32;
+  if (encoding == SOX_ENCODING_FLOAT && size == 32) return Float;
+  if (encoding == SOX_ENCODING_FLOAT && size == 64) return Double;
+  return Unspecified;
 }
 
-static int sox_enc(
-    uint32_t sun_encoding, sox_encoding_t * encoding, unsigned * size)
+static sox_encoding_t sox_enc(uint32_t ft_encoding, unsigned * size)
 {
-  switch (sun_encoding) {
-    case Mulaw_8     : *encoding = SOX_ENCODING_ULAW ; *size =  8; break;
-    case Alaw_8      : *encoding = SOX_ENCODING_ALAW ; *size =  8; break;
-    case Linear_8    : *encoding = SOX_ENCODING_SIGN2; *size =  8; break;
-    case Linear_16   : *encoding = SOX_ENCODING_SIGN2; *size = 16; break;
-    case Linear_24   : *encoding = SOX_ENCODING_SIGN2; *size = 24; break;
-    case Linear_32   : *encoding = SOX_ENCODING_SIGN2; *size = 32; break;
-    case Float       : *encoding = SOX_ENCODING_FLOAT; *size = 32; break;
-    case Double      : *encoding = SOX_ENCODING_FLOAT; *size = 64; break;
-    /* Sun encodings that SoX can read, but not write: */
-    case Adpcm_g721  : *encoding = SOX_ENCODING_G721 ; *size =  4; break;
-    case Adpcm_g723_3: *encoding = SOX_ENCODING_G723 ; *size =  3; break;
-    case Adpcm_g723_5: *encoding = SOX_ENCODING_G723 ; *size =  5; break;
-
-    default: sox_debug("encoding: 0x%x", sun_encoding); return SOX_EOF;
+  switch (ft_encoding) {
+    case Mulaw_8     : *size =  8; return SOX_ENCODING_ULAW;
+    case Alaw_8      : *size =  8; return SOX_ENCODING_ALAW;
+    case Linear_8    : *size =  8; return SOX_ENCODING_SIGN2;
+    case Linear_16   : *size = 16; return SOX_ENCODING_SIGN2;
+    case Linear_24   : *size = 24; return SOX_ENCODING_SIGN2;
+    case Linear_32   : *size = 32; return SOX_ENCODING_SIGN2;
+    case Float       : *size = 32; return SOX_ENCODING_FLOAT;
+    case Double      : *size = 64; return SOX_ENCODING_FLOAT;
+    case Adpcm_g721  : *size =  4; return SOX_ENCODING_G721; /* read-only */
+    case Adpcm_g723_3: *size =  3; return SOX_ENCODING_G723; /* read-only */
+    case Adpcm_g723_5: *size =  5; return SOX_ENCODING_G723; /* read-only */
+    default:                       return SOX_ENCODING_UNKNOWN;
   }
-  return SOX_SUCCESS;
 }
 
 typedef struct {        /* For G72x decoding: */
@@ -128,57 +125,47 @@
 static int startread(sox_format_t * ft)
 {
   priv_t * p = (priv_t * ) ft->priv;
-  uint32_t magic;        /* These 6 uint32_t variables represent a Sun sound */
-  uint32_t hdr_size;     /* header on disk.  The numbers are written as */
-  uint32_t data_size;    /* big-endians.  Any extra bytes (totalling */
-  uint32_t sun_encoding; /* hdr_size - 24) are an "info" field of */
-  uint32_t sample_rate;  /* unspecified nature, usually a string.  By */
+  char     magic[4];     /* These 6 variables represent a Sun sound */
+  uint32_t hdr_size;     /* header on disk.  The uint32_t are written as */
+  uint32_t data_size;    /* big-endians.  At least extra bytes (totalling */
+  uint32_t ft_encoding;  /* hdr_size - FIXED_HDR) are an "info" field of */
+  uint32_t rate;         /* unspecified nature, usually a string.  By */
   uint32_t channels;     /* convention the header size is a multiple of 4. */
-  unsigned bits_per_sample;
-  sox_encoding_t sox_encoding;
+  unsigned i, bits_per_sample;
+  sox_encoding_t encoding;
 
-  sox_readdw(ft, &magic);
-  if (magic == DEC_INV_MAGIC) {
-    sox_debug("Found inverted DEC magic word.");
-    /* Inverted headers are not standard.  Code was probably
-     * left over from pre-standardize period of testing for
-     * endianess.  Its not hurting though.
-     */
-    ft->encoding.reverse_bytes = SOX_IS_BIGENDIAN;
-  }
-  else if (magic == SUN_INV_MAGIC) {
-    sox_debug("Found inverted Sun/NeXT magic word.");
-    ft->encoding.reverse_bytes = SOX_IS_BIGENDIAN;
-  }
-  else if (magic == SUN_MAGIC) {
-    sox_debug("Found Sun/NeXT magic word");
-    ft->encoding.reverse_bytes = SOX_IS_LITTLEENDIAN;
-  }
-  else if (magic == DEC_MAGIC) {
-    sox_debug("Found DEC magic word");
-    ft->encoding.reverse_bytes = SOX_IS_LITTLEENDIAN;
-  }
-  else {
-    sox_fail_errno(ft,SOX_EHDR,"Did not detect valid Sun/NeXT/DEC magic number in header.");
+  if (sox_readchars(ft, magic, sizeof(magic)))
     return SOX_EOF;
+ 
+  for (i = 0; id[i].desc && memcmp(magic, id[i].str, sizeof(magic)); ++i);
+  if (!id[i].desc) {
+    sox_fail_errno(ft, SOX_EHDR, "au: can't find Sun/NeXT/DEC identifier");
+    return SOX_EOF;
   }
+  sox_report("found %s identifier", id[i].desc);
+  ft->encoding.reverse_bytes = id[i].reverse_bytes;
 
-  sox_readdw(ft, &hdr_size);
-  if (hdr_size < SUN_HDRSIZE) {
-    sox_fail_errno(ft, SOX_EHDR, "Sun/NeXT header size too small.");
+  if (sox_readdw(ft, &hdr_size) ||
+      sox_readdw(ft, &data_size) ||   /* Can be SUN_UNSPEC */
+      sox_readdw(ft, &ft_encoding) ||
+      sox_readdw(ft, &rate) ||
+      sox_readdw(ft, &channels))
     return SOX_EOF;
+
+  if (hdr_size < FIXED_HDR) {
+    sox_fail_errno(ft, SOX_EHDR, "header size %u is too small", hdr_size);
+    return SOX_EOF;
   }
-  sox_readdw(ft, &data_size);     /* Can be SUN_UNSPEC */
-  sox_readdw(ft, &sun_encoding);
-  sox_readdw(ft, &sample_rate);
-  sox_readdw(ft, &channels);
+  if (hdr_size < FIXED_HDR + 4)
+    sox_warn("header size %u is too small", hdr_size);
 
-  if (sox_enc(sun_encoding, &sox_encoding, &bits_per_sample) == SOX_EOF) {
-    int n = min(sun_encoding, Unknown_other);
-    sox_fail_errno(ft, SOX_EFMT, "unsupported encoding `%s' (%u)", str[n], sun_encoding);
+  if (!(encoding = sox_enc(ft_encoding, &bits_per_sample))) {
+    int n = min(ft_encoding, Unknown_other);
+    sox_fail_errno(ft, SOX_EFMT, "unsupported encoding `%s' (%#x)", str[n], ft_encoding);
     return SOX_EOF;
   }
-  switch (sun_encoding) {
+
+  switch (ft_encoding) {
     case Adpcm_g721  : p->dec_routine = g721_decoder   ; break;
     case Adpcm_g723_3: p->dec_routine = g723_24_decoder; break;
     case Adpcm_g723_5: p->dec_routine = g723_40_decoder; break;
@@ -189,11 +176,10 @@
     ft->handler.read = dec_read;
   }
 
-  hdr_size -= SUN_HDRSIZE; /* # bytes already read */
-  if (hdr_size > 0) {
-    char * buf = xcalloc(1, hdr_size + 1); /* +1 ensures null-terminated */
-    if (sox_readbuf(ft, buf, hdr_size) != hdr_size) {
-      sox_fail_errno(ft, SOX_EOF, "Unexpected EOF in Sun/NeXT header info.");
+  if (hdr_size > FIXED_HDR) {
+    size_t info_size = hdr_size - FIXED_HDR;
+    char * buf = xcalloc(1, info_size + 1); /* +1 ensures null-terminated */
+    if (sox_readchars(ft, buf, info_size) != SOX_SUCCESS) {
       free(buf);
       return SOX_EOF;
     }
@@ -201,25 +187,26 @@
     free(buf);
   }
   if (data_size == SUN_UNSPEC)
-    data_size = 0;  /* SoX uses 0 for unspecified */
-  return sox_check_read_params( ft, channels, (sox_rate_t)sample_rate,
-      sox_encoding, bits_per_sample, div_bits(data_size, bits_per_sample));
+    data_size = 0;  /* libSoX uses 0 for unspecified */
+  return sox_check_read_params(ft, channels, (sox_rate_t)rate,
+      encoding, bits_per_sample, div_bits(data_size, bits_per_sample));
 }
 
 static int write_header(sox_format_t * ft)
 {
-  char   *comment = cat_comments(ft->comments);
+  char * comment  = cat_comments(ft->comments);
   size_t len      = strlen(comment) + 1;     /* Write out null-terminated */
   size_t info_len = max(4, (len + 3) & ~3u); /* Minimum & multiple of 4 bytes */
   size_t size     = ft->olength? ft->olength : ft->length;
+  int i = ft->encoding.reverse_bytes == SOX_IS_BIGENDIAN? 2 : 0;
   sox_bool error  = sox_false
-  ||sox_writedw(ft, SUN_MAGIC)
-  ||sox_writedw(ft, SUN_HDRSIZE + info_len)
+  ||sox_writechars(ft, id[i].str, sizeof(id[i].str))
+  ||sox_writedw(ft, FIXED_HDR + info_len)
   ||sox_writedw(ft, size? size*(ft->encoding.bits_per_sample >> 3) : SUN_UNSPEC)
-  ||sox_writedw(ft, sun_enc(ft->encoding.bits_per_sample,ft->encoding.encoding))
+  ||sox_writedw(ft, ft_enc(ft->encoding.bits_per_sample, ft->encoding.encoding))
   ||sox_writedw(ft, (unsigned)(ft->signal.rate + .5))
   ||sox_writedw(ft, ft->signal.channels)
-  ||sox_writebuf(ft, comment, len) != len
+  ||sox_writechars(ft, comment, len)
   ||sox_padbytes(ft, info_len - len);
   free(comment);
   return error? SOX_EOF: SOX_SUCCESS;
@@ -234,8 +221,7 @@
     SOX_ENCODING_SIGN2, 8, 16, 24, 32, 0,
     SOX_ENCODING_FLOAT, 32, 64, 0,
     0};
-  static sox_format_handler_t const handler = {
-    SOX_LIB_VERSION_CODE,
+  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
     "PCM file format used widely on Sun systems",
     names, SOX_FILE_BIG_END | SOX_FILE_REWIND,
     startread, sox_rawread, NULL,
--- a/src/formats.h
+++ b/src/formats.h
@@ -26,7 +26,8 @@
   FORMAT(s4)
   FORMAT(sf)
   FORMAT(smp)
-  FORMAT(sndrtool)
+  FORMAT(sounder)
+  FORMAT(soundtool)
   FORMAT(sphere)
   FORMAT(svx)
   FORMAT(txw)
--- a/src/misc.c
+++ b/src/misc.c
@@ -156,8 +156,13 @@
     sox_warn("'%s': overriding encoding size", ft->filename);
   ft->encoding.bits_per_sample = bits_per_sample;
 
-  if (!ft->length && ft->encoding.bits_per_sample && sox_filelength(ft))
-    ft->length = div_bits(sox_filelength(ft) - ft->data_start, ft->encoding.bits_per_sample);
+  if (ft->encoding.bits_per_sample && sox_filelength(ft)) {
+    off_t calculated_length = div_bits(sox_filelength(ft) - ft->data_start, ft->encoding.bits_per_sample);
+    if (!ft->length)
+      ft->length = calculated_length;
+    else if (length != calculated_length)
+      sox_warn("file header gives the total number of samples as %u but file length indicates the number is in fact %u", (unsigned)length, (unsigned)calculated_length); /* FIXME: casts */
+  }
 
   if ( sox_precision(ft->encoding.encoding, ft->encoding.bits_per_sample))
     return SOX_SUCCESS;
@@ -173,8 +178,6 @@
   return (SOX_SAMPLE_MAX >> shift) << shift;
 }
 
-const char sox_readerr[] = "Premature EOF while reading sample file";
-
 /* Lookup table to reverse the bit order of a byte. ie MSB become LSB */
 uint8_t const cswap[256] = {
   0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0,
@@ -209,7 +212,9 @@
 size_t sox_readbuf(sox_format_t * ft, void *buf, sox_size_t len)
 {
   size_t ret = fread(buf, 1, len, ft->fp);
-  return (ferror(ft->fp) || (feof(ft->fp) && ret == 0)) ? 0 : ret;
+  if (ret != len && ferror(ft->fp))
+    sox_fail_errno(ft, errno, "sox_readbuf");
+  return ret;
 }
 
 /* Skip input without seeking. */
@@ -238,7 +243,7 @@
  * Returns number of bytes written.
  */
 
-size_t sox_writebuf(sox_format_t * ft, void const *buf, sox_size_t len)
+size_t sox_writebuf(sox_format_t * ft, void const * buf, sox_size_t len)
 {
   size_t ret = fwrite(buf, 1, len, ft->fp);
   if (ret != len) {
@@ -303,8 +308,6 @@
         if (sox_readbuf(ft, &in, 1) != 1)
         {
             *sc = 0;
-            if (sox_error(ft))
-              sox_fail_errno(ft, errno, sox_readerr);
             return (SOX_EOF);
         }
         if (in == 0 || in == '\n')
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -196,9 +196,7 @@
      */
     ReadSize = sox_readbuf(ft, p->InputBuffer, INPUT_BUFFER_SIZE);
     if (ReadSize < INPUT_BUFFER_SIZE) {
-      if (sox_error(ft))
-        sox_fail_errno(ft, SOX_EOF, "error reading input file");
-      else if (sox_eof(ft))
+      if (sox_eof(ft))
         sox_fail_errno(ft, SOX_EOF, "input file too short");
       return SOX_EOF;
     }
--- a/src/raw.c
+++ b/src/raw.c
@@ -65,8 +65,7 @@
   { \
     sox_size_t n, nread; \
     ctype *data = xmalloc(sizeof(ctype) * len); \
-    if ((nread = sox_read_ ## type ## _buf(ft, (uctype *)data, len)) != len && sox_error(ft)) \
-      sox_fail_errno(ft, errno, sox_readerr); \
+    nread = sox_read_ ## type ## _buf(ft, (uctype *)data, len); \
     for (n = 0; n < nread; n++) \
       *buf++ = cast(data[n], ft->clips); \
     free(data); \
--- a/src/sf.c
+++ b/src/sf.c
@@ -1,232 +1,155 @@
 /*
- * July 5, 1991
- * Copyright 1991 Lance Norskog And Sundry Contributors
- * This source code is freely redistributable and may be used for
- * any purpose.  This copyright notice must be maintained.
- * Lance Norskog And Sundry Contributors are not responsible for
- * the consequences of using this software.
- */
-
-/*
- * libSoX IRCAM SoundFile format handler.
+ * File format: IRCAM SoundFile   (c) 2008 robs@users.sourceforge.net
  *
- * Derived from: libSoX skeleton handler file.
+ * See http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/IRCAM/IRCAM.html
+ *
+ * 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 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,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
  */
 
 #include "sox_i.h"
-#include "sfircam.h"
-
 #include <string.h>
-#include <stdlib.h>
 
-/* Private data for SF file */
-typedef struct sfstuff {
-        struct sfinfo info;
-        /* needed for seek */
-        sox_size_t dataStart;
-} *sf_t;
+/* Magic numbers used in IRCAM audio files */
+static struct {char str[4]; sox_bool reverse_bytes; char const * desc;} id[] = {
+  {"\144\243\001\0", SOX_IS_BIGENDIAN   , "Little-endian VAX (native)"},
+  {"\0\001\243\144", SOX_IS_LITTLEENDIAN, "Big-endian VAX"},
+  {"\144\243\002\0", SOX_IS_LITTLEENDIAN, "Big-endian Sun (native)"},
+  {"\0\002\243\144", SOX_IS_BIGENDIAN   , "Little-endian Sun"},
+  {"\144\243\003\0", SOX_IS_BIGENDIAN   , "Little-endian MIPS (DEC)"},
+  {"\0\003\243\144", SOX_IS_LITTLEENDIAN, "Big-endian MIPS (SGI)"},
+  {"\144\243\004\0", SOX_IS_LITTLEENDIAN, "Big-endian NeXT"},
+  {"    ", 0, NULL}
+};
+#define FIXED_HDR     1024
+#define SF_COMMENT    2        /* code for "comment line" */
 
-/*
- * Read the codes from the sound file, allocate space for the comment and
- * assign its pointer to the comment field in ft.
- */
-static void readcodes(sox_format_t * ft, SFHEADER *sfhead)
-{
-        char *commentbuf = NULL, *sfcharp;
-        sox_size_t bsize;
-        sox_bool finished = sox_false;
-        SFCODE *sfcodep;
+typedef enum {Unspecified,
+  Linear_8 = 0x00001, Alaw_8 = 0x10001, Mulaw_8 = 0x20001, Linear_16 = 0x00002,
+  Linear_24 = 0x00003, Linear_32 = 0x40004, Float = 0x00004, Double = 0x00008
+} ft_encoding_t;
 
-        sfcodep = (SFCODE *) (&sfhead->sfinfo + 1);
-        do {
-                sfcharp = (char *) sfcodep + sizeof(SFCODE);
-                if (ft->encoding.reverse_bytes) {
-                        sfcodep->bsize = sox_swapdw(sfcodep->bsize);
-                        sfcodep->code = sox_swapdw(sfcodep->code);
-                }
-                bsize = sfcodep->bsize - sizeof(SFCODE);
-                switch(sfcodep->code) {
-                case SF_END:
-                        finished = sox_true;
-                        break;
-                case SF_COMMENT:
-                        commentbuf = (char *) xmalloc(bsize + 1);
-                        memcpy(commentbuf, sfcharp, bsize);
-                        commentbuf[bsize] = '\0';
-                        break;
-                }
-                sfcodep = (SFCODE *) (sfcharp + bsize);
-        } while(!finished);
-        append_comments(&ft->comments, commentbuf);
-        free(commentbuf);
-}
+static ft_encoding_t ft_enc(unsigned size, sox_encoding_t encoding)
+{
+  if (encoding == SOX_ENCODING_ULAW  && size ==  8) return Mulaw_8;
+  if (encoding == SOX_ENCODING_ALAW  && size ==  8) return Alaw_8;
+  if (encoding == SOX_ENCODING_SIGN2 && size ==  8) return Linear_8;
+  if (encoding == SOX_ENCODING_SIGN2 && size == 16) return Linear_16;
+  if (encoding == SOX_ENCODING_SIGN2 && size == 24) return Linear_24;
+  if (encoding == SOX_ENCODING_SIGN2 && size == 32) return Linear_32;
+  if (encoding == SOX_ENCODING_FLOAT && size == 32) return Float;
+  if (encoding == SOX_ENCODING_FLOAT && size == 64) return Double;
+  return Unspecified;
+}
 
-static int seek(sox_format_t * ft, sox_size_t offset)
+static sox_encoding_t sox_enc(uint32_t ft_encoding, unsigned * size)
 {
-    sox_size_t new_offset, channel_block, alignment;
-
-    sf_t sf = (sf_t ) ft->priv;
-    new_offset = offset * (ft->encoding.bits_per_sample >> 3);
-    /* Make sure request aligns to a channel block (ie left+right) */
-    channel_block = ft->signal.channels * (ft->encoding.bits_per_sample >> 3);
-    alignment = new_offset % channel_block;
-    /* Most common mistaken is to compute something like
-     * "skip everthing upto and including this sample" so
-     * advance to next sample block in this case.
-     */
-    if (alignment != 0)
-        new_offset += (channel_block - alignment);
-    new_offset += sf->dataStart;
-
-    return sox_seeki(ft, (sox_ssize_t)new_offset, SEEK_SET);
+  switch (ft_encoding) {
+    case Mulaw_8     : *size =  8; return SOX_ENCODING_ULAW;
+    case Alaw_8      : *size =  8; return SOX_ENCODING_ALAW;
+    case Linear_8    : *size =  8; return SOX_ENCODING_SIGN2;
+    case Linear_16   : *size = 16; return SOX_ENCODING_SIGN2;
+    case Linear_24   : *size = 24; return SOX_ENCODING_SIGN2;
+    case Linear_32   : *size = 32; return SOX_ENCODING_SIGN2;
+    case Float       : *size = 32; return SOX_ENCODING_FLOAT;
+    case Double      : *size = 64; return SOX_ENCODING_FLOAT;
+    default:                       return SOX_ENCODING_UNKNOWN;
+  }
 }
 
-/*
- * Do anything required before you start reading samples.
- * Read file header.
- *      Find out sampling rate,
- *      size and encoding of samples,
- *      mono/stereo/quad.
- */
 static int startread(sox_format_t * ft)
 {
-        sf_t sf = (sf_t) ft->priv;
-        SFHEADER sfhead;
-        int rc;
-        int samplesize = 0;
+  char     magic[4];
+  float    rate;
+  uint32_t channels, ft_encoding;
+  unsigned i, bits_per_sample;
+  sox_encoding_t encoding;
+  uint16_t code, size;
 
-        if (sox_readbuf(ft, &sfhead, sizeof(sfhead)) != sizeof(sfhead))
-        {
-                sox_fail("unexpected EOF in SF header");
-                return(SOX_EOF);
-        }
-        memcpy(&sf->info, &sfhead.sfinfo, sizeof(struct sfinfo));
-        if (ft->encoding.reverse_bytes) {
-                sox_swapf(&sf->info.sf_srate);
-                sf->info.sf_packmode = sox_swapdw(sf->info.sf_packmode);
-                sf->info.sf_chans = sox_swapdw(sf->info.sf_chans);
-        }
-        if ((sfmagic1(&sfhead) != SF_MAGIC1) ||
-            (sfmagic2(&sfhead) != SF_MAGIC2)) {
-          sox_fail_errno(ft, SOX_EHDR, "sf: can't find IRCAM identifier");
-          return SOX_EOF;
-        }
+  if (sox_readchars(ft, magic, sizeof(magic)))
+    return SOX_EOF;
+ 
+  for (i = 0; id[i].desc && memcmp(magic, id[i].str, sizeof(magic)); ++i);
+  if (!id[i].desc) {
+    sox_fail_errno(ft, SOX_EHDR, "sf: can't find IRCAM identifier");
+    return SOX_EOF;
+  }
+  sox_report("found %s identifier", id[i].desc);
+  ft->encoding.reverse_bytes = id[i].reverse_bytes;
 
-        /*
-         * If your format specifies or your file header contains
-         * any of the following information.
-         */
-        ft->signal.rate = sf->info.sf_srate;
-        switch(sf->info.sf_packmode) {
-                case SF_SHORT:
-                        ft->encoding.bits_per_sample = 16;
-                        ft->encoding.encoding = SOX_ENCODING_SIGN2;
-                        samplesize = 2;
-                        break;
-                case SF_FLOAT:
-                        ft->encoding.bits_per_sample = 16;
-                        ft->encoding.encoding = SOX_ENCODING_FLOAT;
-                        samplesize = sizeof(float);
-                        break;
-                default:
-                        sox_fail("Soundfile input: unknown format 0x%x",
-                                sf->info.sf_packmode);
-                        return(SOX_EOF);
-        }
-        ft->signal.channels = (int) sf->info.sf_chans;
-
-        if (ft->signal.channels == 0)
-            ft->signal.channels = 1;
-
-        /* Read codes and print as comments. */
-        readcodes(ft, &sfhead);
-
-        /* Needed for rawread() */
-        rc = sox_rawstartread(ft);
-
-/* Need length for seeking */
-        if(ft->seekable){
-                ft->length = sox_filelength(ft)/samplesize;
-                sf->dataStart = sox_tell(ft);
-        } else {
-                ft->length = 0;
-        }
-
-        return(rc);
+  if (sox_readf(ft, &rate) || sox_readdw(ft, &channels) || sox_readdw(ft, &ft_encoding))
+    return SOX_EOF;
+  
+  if (!(encoding = sox_enc(ft_encoding, &bits_per_sample))) {
+    sox_fail_errno(ft, SOX_EFMT, "sf: unsupported encoding %#x)", ft_encoding);
+    return SOX_EOF;
+  }
+  do {
+    if (sox_readw(ft, &code) || sox_readw(ft, &size))
+      return SOX_EOF;
+    if (code == SF_COMMENT) {
+      char * buf = xcalloc(1, (size_t)size + 1); /* +1 ensures null-terminated */
+      if (sox_readchars(ft, buf, size) != SOX_SUCCESS) {
+        free(buf);
+        return SOX_EOF;
+      }
+      append_comments(&ft->comments, buf);
+      free(buf);
+    }
+    else if (sox_skipbytes(ft, size))
+      return SOX_EOF;
+  } while (code);
+  if (sox_skipbytes(ft, FIXED_HDR - (sox_size_t)sox_tell(ft)))
+    return SOX_EOF;
+  
+  return sox_check_read_params(ft, channels, rate, encoding, bits_per_sample, (off_t)0);
 }
 
-static int startwrite(sox_format_t * ft)
+static int write_header(sox_format_t * ft)
 {
-        sf_t sf = (sf_t) ft->priv;
-        SFHEADER sfhead;
-        SFCODE *sfcodep;
-        char *sfcharp;
-        int rc;
-        char * comment = cat_comments(ft->comments);
-
-        /* Needed for rawwrite() */
-        rc = sox_rawstartwrite(ft);
-        if (rc)
-            return rc;
-
-        sf->info.magic_union._magic_bytes.sf_magic1 = SF_MAGIC1;
-        sf->info.magic_union._magic_bytes.sf_magic2 = SF_MAGIC2;
-        sf->info.magic_union._magic_bytes.sf_param = 0;
-
-        /* This file handler can handle both big and little endian data */
-        if (SOX_IS_LITTLEENDIAN)
-            sf->info.magic_union._magic_bytes.sf_machine = SF_VAX;
-        else
-            sf->info.magic_union._magic_bytes.sf_machine = SF_SUN;
-
-        sf->info.sf_srate = ft->signal.rate;
-        if (ft->encoding.bits_per_sample == 32 &&
-            ft->encoding.encoding == SOX_ENCODING_FLOAT) {
-                sf->info.sf_packmode = SF_FLOAT;
-        } else {
-                sf->info.sf_packmode = SF_SHORT;
-                /* Default to signed words */
-                ft->encoding.bits_per_sample = 16;
-                ft->encoding.encoding = SOX_ENCODING_SIGN2;
-        }
-
-        sf->info.sf_chans = ft->signal.channels;
-
-        /* Clean out structure so unused areas will remain constain  */
-        /* between different coverts and not rely on memory contents */
-        memset (&sfhead, 0, sizeof(SFHEADER));
-        memcpy(&sfhead.sfinfo, &sf->info, sizeof(struct sfinfo));
-        sfcodep = (SFCODE *) (&sfhead.filler[SF_INFO_LEN+1]);
-        sfcodep->code = SF_COMMENT;
-        sfcodep->bsize = strlen(comment) + sizeof(SFCODE);
-        while (sfcodep->bsize % 4)
-                sfcodep->bsize++;
-        sfcharp = (char *) sfcodep;
-        strcpy(sfcharp + sizeof(SFCODE), comment);
-        free(comment);
-        sfcodep = (SFCODE *) (sfcharp + sfcodep->bsize);
-        sfcodep->code = SF_END;
-        sfcodep->bsize = sizeof(SFCODE);
-        sfcharp = (char *) sfcodep + sizeof(SFCODE);
-        sox_writebuf(ft, &sfhead, sizeof(SFHEADER));
-
-        return(SOX_SUCCESS);
+  char * comment  = cat_comments(ft->comments);
+  size_t len      = min(FIXED_HDR - 26, strlen(comment)) + 1; /* null-terminated */
+  size_t info_len = max(4, (len + 3) & ~3u); /* Minimum & multiple of 4 bytes */
+  int i = ft->encoding.reverse_bytes == SOX_IS_BIGENDIAN? 0 : 2;
+  sox_bool error  = sox_false
+  ||sox_writechars(ft, id[i].str, sizeof(id[i].str))
+  ||sox_writef(ft, ft->signal.rate)
+  ||sox_writedw(ft, ft->signal.channels)
+  ||sox_writedw(ft, ft_enc(ft->encoding.bits_per_sample, ft->encoding.encoding))
+  ||sox_writew(ft, SF_COMMENT)
+  ||sox_writew(ft, info_len)
+  ||sox_writechars(ft, comment, len)
+  ||sox_padbytes(ft, FIXED_HDR - 20 - len);
+  free(comment);
+  return error? SOX_EOF: SOX_SUCCESS;
 }
 
 SOX_FORMAT_HANDLER(sf)
 {
   static char const * const names[] = {"sf", "ircam", NULL};
-  static unsigned const encodings[] = {
-    SOX_ENCODING_SIGN2, 16, 0,
-    SOX_ENCODING_FLOAT, 32, 0,
+  static unsigned const write_encodings[] = {
+    SOX_ENCODING_ULAW, 8, 0,
+    SOX_ENCODING_ALAW, 8, 0,
+    SOX_ENCODING_SIGN2, 8, 16, 24, 32, 0,
+    SOX_ENCODING_FLOAT, 32, 64, 0,
     0};
-  static sox_format_handler_t const handler = {
-    SOX_LIB_VERSION_CODE,
-    "Institut de Recherche et Coordination Acoustique/Musique Sound Description Interchange Format",
-    names, 0,
-    startread, sox_rawread, sox_rawstopread,
-    startwrite, sox_rawwrite, sox_rawstopwrite,
-    seek, encodings, NULL
+  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
+    "Institut de Recherche et Coordination Acoustique/Musique",
+    names, SOX_FILE_LIT_END,
+    startread, sox_rawread, NULL,
+    write_header, sox_rawwrite, NULL,
+    sox_rawseek, write_encodings, NULL
   };
   return &handler;
 }
--- a/src/sfircam.h
+++ /dev/null
@@ -1,222 +1,0 @@
-/*                              SFHEADER.H                              */
-
-/* definitions and structures needed for manipulating soundfiles.
- */
-
-#define SIZEOF_HEADER 1024
-#define SF_BUFSIZE      (16*1024) /* used only in play */
-#define SF_MAXCHAN      4
-#define MAXCOMM 512
-#define MINCOMM 256
-
-#define SF_MAGIC1 0144
-#define SF_MAGIC2 0243
-
-/* Definition of SF_MACHINE and SF_MAGIC
- *
- * Note that SF_MAGIC always has SF_MAGIC1 as its first byte, SF_MAGIC2 as its
- * second, SF_MACHINE as its third, and zero as its fourth.  Separate defines
- * are needed because byte order is different on different machines.
- */
-#define SF_VAX 1
-#define SF_SUN 2
-#define SF_MIPS 3
-#define SF_NEXT 4
-#ifdef vax
-#define SF_MACHINE SF_VAX
-#define SF_MAGIC ((uint32_t)(SF_MAGIC1 | SF_MAGIC2 << 8 | SF_MACHINE << 16))
-#endif
-#ifdef sun
-#define SF_MACHINE SF_SUN
-#define SF_MAGIC ((uint32_t)(SF_MAGIC1 << 24 | SF_MAGIC2 << 16 | SF_MACHINE << 8))
-#endif
-#ifdef mips
-#define SF_MACHINE SF_MIPS
-#define SF_MAGIC ((uint32_t)(SF_MAGIC1 | SF_MAGIC2 << 8 | SF_MACHINE << 16))
-#endif
-#ifdef NeXT
-#define SF_MACHINE SF_NEXT
-#define SF_MAGIC ((uint32_t)(SF_MAGIC1 << 24 | SF_MAGIC2 << 16 | SF_MACHINE << 8))
-#endif
-
-
-/* Packing modes, as stored in the SFHEADER.sf_packmode field
- *
- * For each packing mode, the lower-order short is the number of bytes per
- * sample, and for backward compatibility, SF_SHORT and SF_FLOAT have
- * high-order short = 0 so overall they're the bytes per sample, but that's not
- * true for all SF_'s.  Thus while the "sfclass" macro still returns a unique
- * ID for each packing mode, the new "sfsamplesize" macro should be used to get
- * the bytes per sample.
- *
- * Note that SF_X == SFMT_X in most, but not all, cases, because MIT changed
- * SFMT_FLOAT and we kept SF_FLOAT for compatibility with existing sound files.
- *
- * Possible values of sf_packmode:
- */
-#define SF_CHAR  ((uint32_t) sizeof(char))
-#define SF_ALAW  ((uint32_t) sizeof(char) | 0x10000)
-#define SF_ULAW  ((uint32_t) sizeof(char) | 0x20000)
-#define SF_SHORT ((uint32_t) sizeof(short))
-#define SF_LONG  ((uint32_t) sizeof(long) | 0x40000)
-#define SF_FLOAT ((uint32_t) sizeof(float))
-
-/* For marking data after fixed section of soundfile header -- see man (3carl)
- * sfcodes and defintions of SFCODE and related structures, below.
- */
-#define SF_END 0            /* Meaning no more information */
-#define SF_MAXAMP 1         /* Meaning maxamp follows */
-#define SF_COMMENT 2        /* code for "comment line" */
-#define SF_PVDATA      3
-#define SF_AUDIOENCOD  4
-#define SF_CODMAX      4
-
-/*
- * DEFINITION OF SFHEADER FORMAT
- *
- * The first four bytes are the magic information for the sound file.  They
- * can be accessed, via a union, either as a structure of four unsigned bytes
- * sf_magic1, sf_magic2, sf_machine, sf_param, or as the single long sf_magic.
- * sf_magic is for backward compatibility; it should be SF_MAGIC as defined
- * above.
- */
-
-/* Define this value to get around by padding issues. */
-#define SF_INFO_LEN 16
-
-struct sfinfo {
-    union magic_union {
-        struct {
-            unsigned char sf_magic1;  /* byte 1 of magic */
-            unsigned char sf_magic2;  /* 2 */
-            unsigned char sf_machine; /* 3 */
-            unsigned char sf_param;   /* 4 */
-        } _magic_bytes;
-        uint32_t sf_magic;            /* magic as a 4-byte long */
-    } magic_union;
-    float     sf_srate;
-    uint32_t          sf_chans;
-    uint32_t          sf_packmode;
-};
-
-typedef union sfheader {
-        struct sfinfo sfinfo;
-        char    filler[SIZEOF_HEADER];
-} SFHEADER;
-
-/*
- * Definition of SFCODE and related data structs
- *
- * Two routines in libbicsf/sfcodes.c, getsfcode() and putsfcode()
- * are used to insert additionnal information into a header
- * or to retreive such information. See man sfcodes.
- *
- * 10/90 pw
- *      These routines are now part of libcarl/sfcodes.c
- */
-
-typedef struct sfcode {
-        short   code;
-        short   bsize;
-} SFCODE;
-
-typedef struct Sfmaxamp {
-        float   value[SF_MAXCHAN];
-        uint32_t samploc[SF_MAXCHAN];
-        uint32_t timetag;
-} SFMAXAMP;
-
-typedef struct sfcomment {
-        char    comment[MAXCOMM];
-} SFCOMMENT;
-
-typedef struct {                  /* this code written by pvanal */
-        short   frameSize;
-        short   frameIncr;
-} SFPVDATA;
-
-typedef struct {                  /*     ditto                    */
-        short   encoding;
-        short   grouping;
-} SFAUDIOENCOD;
-
-/*
- * DEFINITION OF MACROS TO GET HEADER INFO
- *     x is a pointer to SFHEADER
- *
- * For backward compatibility in MIT Csound code, sfmagic(x) still provides
- * access to the first long of SFHEADER x.  It can be compared to SF_MAGIC,
- * which is defined machine-dependently (above) to always be the right four
- * bytes in the right order.
- *
- * sfclass(x) returns one of SF_SHORT, SF_FLOAT etc. defined above, while
- * sfsamplesize(x) returns just the bytes per object, the lower-order short of
- * sf_packmode.
- */
-#define sfmagic(x) ((x)->sfinfo.magic_union.sf_magic)
-#define sfmagic1(x) ((x)->sfinfo.magic_union._magic_bytes.sf_magic1)
-#define sfmagic2(x) ((x)->sfinfo.magic_union._magic_bytes.sf_magic2)
-#define sfmachine(x) ((x)->sfinfo.magic_union._magic_bytes.sf_machine)
-#define sfparam(x) ((x)->sfinfo.magic_union._magic_bytes.sf_param)
-#define sfsrate(x) ((x)->sfinfo.sf_srate)
-#define sfchans(x) ((x)->sfinfo.sf_chans)
-#define sfclass(x) ((x)->sfinfo.sf_packmode)
-#define sfsamplesize(x) ((size_t) ((x)->sfinfo.sf_packmode & 0xFFFF))
-#define sfbsize(x) ((x)->sox_size - sizeof(SFHEADER))
-
-/*
- * Macros for testing soundfiles
- */
-/* True if soundfile and good arch */
-#define ismagic(x) ((sfmagic1(x) == SF_MAGIC1) && \
-        (sfmagic2(x) == SF_MAGIC2) && \
-        (sfmachine(x) == SF_MACHINE))
-
-/* True if soundfile */
-#define isforeignmagic(x) ((sfmagic1(x) == SF_MAGIC1) && \
-        (sfmagic2(x) == SF_MAGIC2))
-
-/* True if soundfile */
-#define issoundfile(x)  ((sfmagic1(x) == SF_MAGIC1) && \
-        (sfmagic2(x) == SF_MAGIC2))
-
-/* True if soundfile and foreign arch */
-#define isforeignsoundfile(x) ((sfmagic1(x) == SF_MAGIC1) && \
-        (sfmagic2(x) == SF_MAGIC2) && \
-        (sfmachine(x) != SF_MACHINE))
-
-/* True if foreign arch */
-#define isforeign(x) (sfmachine(x) != SF_MACHINE)
-
-
-/*
- * The macros for opening soundfiles have been rewritten as C routines.
- * In order to preserve compatibility, we supply the following new macros
- */
-
-#define readopensf(name,fd,sfh,sfst,prog,result) \
-        result = (fd = openrosf(name, &sfh, &sfst, prog)) < 0 ? fd : 0;
-
-#define freadopensf(name,fp,sfh,sfst,prog,result) \
-        result = fopenrosf(name, &fp, &sfh, &sfst, prog);
-
-#define wropensf(name,fd,sfh,prog,result) \
-        result = (fd = openwosf(name, &sfh, prog)) < 0 ? fd : 0;
-
-#define rdwropensf(name,fd,sfh,sfst,prog,result) \
-        result = (fd = openrwsf(name, &sfh, &sfst, prog)) < 0 ? fd : 0;
-
-
-/*
- * Definition of macro to get MAXAMP and COMMENT info
- *
- * sfm is ptr to SFMAXAMP
- * sfst is the address of a stat struct
- */
-
-#define sfmaxamp(mptr,chan) (mptr)->value[chan]
-#define sfmaxamploc(mptr,chan) (mptr)->samploc[chan]
-#define sfmaxamptime(x) (x)->timetag
-#define ismaxampgood(x,s) (sfmaxamptime(x) >= (s)->sox_mtime)
-#define sfcomm(x,n) (x)->comment[n]
-
--- a/src/sndrtool.c
+++ /dev/null
@@ -1,178 +1,0 @@
-/*
- * Sounder/Sndtool format handler: W V Neisius, February 1992
- *
- * June 28, 93: force output to mono.
- * 
- * March 3, 1999 - cbagwell@sprynet.com
- *   Forced extra comment fields to zero.
- */
-
-#include "sox_i.h"
-
-#include <math.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-
-/* Private data used by writer */
-typedef struct sndpriv {
-  sox_size_t nsamples;
-  sox_size_t dataStart;
-} *snd_t;
-
-static void sndtwriteheader(sox_format_t * ft, sox_size_t nsamples)
-{
-  char name_buf[97];
-
-  /* sndtool header */
-  sox_writes(ft, "SOUND");      /* magic */
-  sox_writeb(ft, 0x1a);
-  sox_writew(ft, 0);    /* hGSound */
-  sox_writedw(ft, nsamples);
-  sox_writedw(ft, 0);
-  sox_writedw(ft, nsamples);
-  sox_writew(ft, (unsigned)(ft->signal.rate + .5));
-  sox_writew(ft, 0);
-  sox_writew(ft, 10);
-  sox_writew(ft, 4);
-  memset(name_buf, 0, 96);
-  sprintf(name_buf, "%.62s - File created by SoX", ft->filename);
-  sox_writebuf(ft, name_buf, 96);
-}
-
-static int seek(sox_format_t * ft, sox_size_t offset)
-{
-  sox_size_t new_offset, channel_block, alignment;
-  snd_t snd = (snd_t) ft->priv;
-
-  new_offset = offset * (ft->encoding.bits_per_sample >> 3);
-  /* Make sure request aligns to a channel block (ie left+right) */
-  channel_block = ft->signal.channels * (ft->encoding.bits_per_sample >> 3);
-  alignment = new_offset % channel_block;
-  /* Most common mistaken is to compute something like
-   * "skip everthing upto and including this sample" so
-   * advance to next sample block in this case.
-   */
-  if (alignment != 0)
-    new_offset += (channel_block - alignment);
-  new_offset += snd->dataStart;
-
-  return sox_seeki(ft, (sox_ssize_t) new_offset, SEEK_SET);
-}
-
-static int startread(sox_format_t * ft)
-{
-  snd_t snd = (snd_t) ft->priv;
-
-  char buf[96];
-
-  unsigned short rate;
-  int rc;
-
-  /* Needed for rawread() */
-  rc = sox_rawstartread(ft);
-  if (rc)
-    return rc;
-
-  rate = 0;
-
-  /* determine file type */
-  /* if first 5 bytes == SOUND then this is probably a sndtool sound */
-  /* if first word (16 bits) == 0 
-   * and second word is between 4000 & 25000 then this is sounder sound */
-  /* otherwise, its probably raw, not handled here */
-
-  if (sox_readbuf(ft, buf, 2) != 2) {
-    sox_fail_errno(ft, errno, "SND: unexpected EOF");
-    return (SOX_EOF);
-  }
-  if (strncmp(buf, "\0\0", 2) == 0) {
-    /* sounder */
-    sox_readw(ft, &rate);
-    if (rate < 4000 || rate > 25000) {
-      sox_fail_errno(ft, SOX_EFMT, "SND: sample rate out of range");
-      return (SOX_EOF);
-    }
-    sox_seeki(ft, 4, SEEK_CUR);
-  } else {
-    /* sndtool ? */
-    sox_readbuf(ft, &buf[2], 6);
-    if (strncmp(buf, "SOUND", 5)) {
-      sox_fail_errno(ft, SOX_EFMT, "SND: unrecognized SND format");
-      return (SOX_EOF);
-    }
-    sox_seeki(ft, 12, SEEK_CUR);
-    sox_readw(ft, &rate);
-    sox_seeki(ft, 6, SEEK_CUR);
-    if (sox_readbuf(ft, buf, 96) != 96) {
-      sox_fail_errno(ft, SOX_EHDR, "SND: unexpected EOF in SND header");
-      return (SOX_EOF);
-    }
-    sox_debug("comments: %.96s", buf);
-  }
-
-  ft->signal.channels = 1;
-  ft->signal.rate = rate;
-  ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
-  ft->encoding.bits_per_sample = 8;
-
-  snd->dataStart = sox_tell(ft);
-  ft->length = sox_filelength(ft) - snd->dataStart;
-
-  return (SOX_SUCCESS);
-}
-
-static int startwrite(sox_format_t * ft)
-{
-  snd_t p = (snd_t) ft->priv;
-  int rc;
-
-  /* Needed for rawwrite() */
-  rc = sox_rawstartwrite(ft);
-  if (rc)
-    return rc;
-
-  /* write header */
-  p->nsamples = 0;
-  sndtwriteheader(ft, 0);
-
-  return (SOX_SUCCESS);
-}
-
-static sox_size_t write_samples(sox_format_t * ft, const sox_sample_t * buf,
-                                sox_size_t len)
-{
-  snd_t p = (snd_t) ft->priv;
-
-  p->nsamples += len;
-  return sox_rawwrite(ft, buf, len);
-}
-
-static int stopwrite(sox_format_t * ft)
-{
-  snd_t p = (snd_t) ft->priv;
-
-  /* fixup file sizes in header */
-  if (sox_seeki(ft, 0, 0) != 0) {
-    sox_fail_errno(ft, errno,
-                   "can't rewind output file to rewrite SND header");
-    return SOX_EOF;
-  }
-  sndtwriteheader(ft, p->nsamples);
-  return (SOX_SUCCESS);
-}
-
-SOX_FORMAT_HANDLER(sndrtool)
-{
-  static char const * const names[] = {"sndt", NULL};
-  static unsigned const write_encodings[] = {SOX_ENCODING_UNSIGNED, 8, 0, 0};
-  static sox_format_handler_t const handler = {
-    SOX_LIB_VERSION_CODE,
-    "Handles Sndtool and Sounder files",
-    names, SOX_FILE_LIT_END | SOX_FILE_MONO,
-    startread, sox_rawread, sox_rawstopread,
-    startwrite, write_samples, stopwrite,
-    seek, write_encodings, NULL
-  };
-  return &handler;
-}
--- /dev/null
+++ b/src/sounder.c
@@ -1,0 +1,56 @@
+/*
+ * Sounder format handler          (c) 2008 robs@users.sourceforge.net
+ * Based on description in soundr3b.zip.
+ *
+ * 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 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,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+ */
+
+#include "sox_i.h"
+
+static int start_read(sox_format_t * ft)
+{
+  uint16_t type, rate;
+
+  if (sox_readw(ft, &type) || sox_readw(ft, &rate) || sox_skipbytes(ft, 4))
+    return SOX_EOF;
+  if (type) {
+    sox_fail_errno(ft, SOX_EHDR, "invalid Sounder header");
+    return SOX_EOF;
+  }
+  return sox_check_read_params(ft, 1, (sox_rate_t)rate, SOX_ENCODING_UNSIGNED, 8, (off_t)0);
+}
+
+static int write_header(sox_format_t * ft)
+{
+  return sox_writew(ft, 0)   /* sample type */
+      || sox_writew(ft, min(65535, (unsigned)(ft->signal.rate + .5)))
+      || sox_writew(ft, 10)  /* speaker driver volume */
+      || sox_writew(ft, 4)?  /* speaker driver DC shift */
+      SOX_EOF : SOX_SUCCESS;
+}
+
+SOX_FORMAT_HANDLER(sounder)
+{
+  static char const * const names[] = {"sndr", NULL};
+  static unsigned const write_encodings[] = {SOX_ENCODING_UNSIGNED, 8, 0, 0};
+  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
+    "8-bit linear audio as used by Aaron Wallace's `Sounder' of 1991",
+    names, SOX_FILE_LIT_END | SOX_FILE_MONO,
+    start_read, sox_rawread, NULL,
+    write_header, sox_rawwrite, NULL,
+    sox_rawseek, write_encodings, NULL
+  };
+  return &handler;
+}
--- /dev/null
+++ b/src/soundtool.c
@@ -1,0 +1,79 @@
+/*
+ * Sounder format handler          (c) 2008 robs@users.sourceforge.net
+ * Based on description in sndtl26.zip.
+ *
+ * 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 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,
+ * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
+ */
+
+#include "sox_i.h"
+#include <string.h>
+
+static char const ID1[6] = "SOUND\x1a";
+#define text_field_len 96  /* Includes null-terminator */
+
+static int start_read(sox_format_t * ft)
+{
+  char id1[sizeof(ID1)], comments[text_field_len + 1];
+  uint32_t nsamples;
+  uint16_t rate;
+
+  if (sox_readchars(ft, id1, sizeof(ID1)) ||
+      sox_skipbytes(ft, 10) || sox_readdw(ft, &nsamples) ||
+      sox_readw(ft, &rate) || sox_skipbytes(ft, 6) ||
+      sox_readchars(ft, comments, text_field_len))
+    return SOX_EOF;
+  if (memcmp(ID1, id1, sizeof(id1))) {
+    sox_fail_errno(ft, SOX_EHDR, "soundtool: can't find SoundTool identifier");
+    return SOX_EOF;
+  }
+  comments[text_field_len] = '\0'; /* Be defensive against incorrect files */
+  append_comments(&ft->comments, comments);
+  return sox_check_read_params(ft, 1, (sox_rate_t)rate, SOX_ENCODING_UNSIGNED, 8, (off_t)0);
+}
+
+static int write_header(sox_format_t * ft)
+{
+  char * comment = cat_comments(ft->comments);
+  char text_buf[text_field_len];
+  sox_size_t length = ft->olength? ft->olength:ft->length;
+
+  memset(text_buf, 0, sizeof(text_buf));
+  strncpy(text_buf, comment, text_field_len - 1);
+  free(comment);
+  return sox_writechars(ft, ID1, sizeof(ID1))
+      || sox_writew  (ft, 0)      /* GSound: not used */
+      || sox_writedw (ft, length) /* length of complete sample */
+      || sox_writedw (ft, 0)      /* first byte to play from sample */
+      || sox_writedw (ft, length) /* first byte NOT to play from sample */
+      || sox_writew  (ft, min(65535, (unsigned)(ft->signal.rate + .5)))
+      || sox_writew  (ft, 0)      /* sample size/type */
+      || sox_writew  (ft, 10)     /* speaker driver volume */
+      || sox_writew  (ft, 4)      /* speaker driver DC shift */
+      || sox_writechars(ft, text_buf, sizeof(text_buf))?  SOX_EOF:SOX_SUCCESS;
+}
+
+SOX_FORMAT_HANDLER(soundtool)
+{
+  static char const * const names[] = {"sndt", NULL};
+  static unsigned const write_encodings[] = {SOX_ENCODING_UNSIGNED, 8, 0, 0};
+  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
+    "8-bit linear audio as used by Martin Hepperle's `SoundTool' of 1991/2",
+    names, SOX_FILE_LIT_END | SOX_FILE_MONO | SOX_FILE_REWIND,
+    start_read, sox_rawread, NULL,
+    write_header, sox_rawwrite, NULL,
+    sox_rawseek, write_encodings, NULL
+  };
+  return &handler;
+}
--- a/src/sox.c
+++ b/src/sox.c
@@ -288,7 +288,7 @@
   }
 
   if (full) {
-    if (ft->encoding.bits_per_sample > 8)
+    if (ft->encoding.bits_per_sample > 8 || (ft->handler.flags & SOX_FILE_ENDIAN))
       fprintf(output, "Endian Type    : %s\n",
           ft->encoding.reverse_bytes != SOX_IS_BIGENDIAN ? "big" : "little");
     if (ft->encoding.bits_per_sample)
@@ -1266,11 +1266,11 @@
 static sox_bool parse_gopts_and_fopts(file_t f, int argc, char **argv)
 {
   while (sox_true) {
-    int option_index;
+    int c, option_index;
     int i; /* sscanf silently accepts negative numbers for %u :( */
     char dummy;     /* To check for extraneous chars in optarg. */
 
-    switch (getopt_long(argc, argv, getoptstr, long_options, &option_index)) {
+    switch (c=getopt_long(argc, argv, getoptstr, long_options, &option_index)) {
     case -1:        /* @ one of: file-name, effect name, end of arg-list. */
       return sox_false; /* i.e. not null file. */
 
@@ -1306,10 +1306,12 @@
         break;
 
       case 5:
+        if (f->encoding.reverse_bytes != SOX_OPTION_DEFAULT || f->encoding.opposite_endian)
+          usage("only one endian option per file is allowed");
         switch (enum_option(option_index, endian_options)) {
           case ENDIAN_little: f->encoding.reverse_bytes = SOX_IS_BIGENDIAN; break;
           case ENDIAN_big: f->encoding.reverse_bytes = SOX_IS_LITTLEENDIAN; break;
-          case ENDIAN_swap: f->encoding.reverse_bytes = sox_true; break;
+          case ENDIAN_swap: f->encoding.opposite_endian = sox_true; break;
         }
         break;
 
@@ -1419,9 +1421,15 @@
         f->encoding.bits_per_sample = 8;
       break;
 
-    case 'L': f->encoding.reverse_bytes   = SOX_IS_BIGENDIAN;    break;
-    case 'B': f->encoding.reverse_bytes   = SOX_IS_LITTLEENDIAN; break;
-    case 'x': f->encoding.reverse_bytes   = SOX_OPTION_YES;      break;
+    case 'L': case 'B': case 'x':
+      if (f->encoding.reverse_bytes != SOX_OPTION_DEFAULT || f->encoding.opposite_endian)
+        usage("only one endian option per file is allowed");
+      switch (c) {
+        case 'L': f->encoding.reverse_bytes   = SOX_IS_BIGENDIAN;    break;
+        case 'B': f->encoding.reverse_bytes   = SOX_IS_LITTLEENDIAN; break;
+        case 'x': f->encoding.opposite_endian = sox_true;            break;
+      }
+      break;
     case 'X': f->encoding.reverse_bits    = SOX_OPTION_YES;      break;
     case 'N': f->encoding.reverse_nibbles = SOX_OPTION_YES;      break;
 
--- a/src/sox.h
+++ b/src/sox.h
@@ -232,6 +232,7 @@
    * indicate to the libSoX core if they have a preference using
    * SOX_FILE_xxx flags.
    */
+  sox_bool opposite_endian;
   sox_option_t reverse_bytes;    /* endiannesses... */
   sox_option_t reverse_nibbles;
   sox_option_t reverse_bits;
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -149,6 +149,7 @@
 int sox_reads(sox_format_t * ft, char *c, sox_size_t len);
 int sox_writes(sox_format_t * ft, char const * c);
 void set_signal_defaults(sox_signalinfo_t * signal);
+#define sox_writechars(ft, chars, len) (sox_writebuf(ft, chars, len) == len? SOX_SUCCESS : SOX_EOF)
 
 sox_size_t sox_read_b_buf(sox_format_t * ft, uint8_t *buf, sox_size_t len);
 sox_size_t sox_read_w_buf(sox_format_t * ft, uint16_t *buf, sox_size_t len);
@@ -164,20 +165,23 @@
 sox_size_t sox_write_f_buf(sox_format_t * ft, float *buf, sox_size_t len);
 sox_size_t sox_write_df_buf(sox_format_t * ft, double *buf, sox_size_t len);
 
-#define sox_readb(ft, ub) (sox_read_b_buf(ft, ub, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+int sox_read3(sox_format_t * ft, uint24_t * u3);
+int sox_readb(sox_format_t * ft, uint8_t * ub);
+int sox_readdf(sox_format_t * ft, double * d);
+int sox_readdw(sox_format_t * ft, uint32_t * udw);
+int sox_readf(sox_format_t * ft, float * f);
+int sox_readw(sox_format_t * ft, uint16_t * uw);
+int sox_readchars(sox_format_t * ft, char * chars, sox_size_t len);
+
+int sox_write3(sox_format_t * ft, unsigned u3);
 int sox_writeb(sox_format_t * ft, unsigned ub);
-#define sox_readw(ft, uw) (sox_read_w_buf(ft, uw, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
-int sox_writew(sox_format_t * ft, unsigned uw);
+int sox_writedw(sox_format_t * ft, unsigned udw);
+int sox_writef(sox_format_t * ft, double f);
 int sox_writesb(sox_format_t * ft, signed);
 int sox_writesw(sox_format_t * ft, signed);
-#define sox_read3(ft, u3) (sox_read_3_buf(ft, u3, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
-int sox_write3(sox_format_t * ft, unsigned u3);
-#define sox_readdw(ft, udw) (sox_read_dw_buf(ft, udw, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
-int sox_writedw(sox_format_t * ft, unsigned udw);
-#define sox_readf(ft, f) (sox_read_f_buf(ft, f, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
-int sox_writef(sox_format_t * ft, float f);
-#define sox_readdf(ft, d) (sox_read_df_buf(ft, d, 1) == 1 ? SOX_SUCCESS : SOX_EOF)
+int sox_writew(sox_format_t * ft, unsigned uw);
 int sox_writedf(sox_format_t * ft, double d);
+
 int sox_seeki(sox_format_t * ft, sox_ssize_t to_sample, int whence);
 int sox_offset_seek(sox_format_t * ft, off_t byte_offset, sox_size_t to_sample);
 sox_size_t sox_filelength(sox_format_t * ft);
@@ -296,8 +300,6 @@
 
 extern sox_globals_t sox_globals;
 extern sox_effects_globals_t sox_effects_globals;
-
-extern const char sox_readerr[];
 extern uint8_t const cswap[256];
 
 
--- a/src/test-comments
+++ b/src/test-comments
@@ -27,17 +27,10 @@
 cp $tmp.au $tmp.comment.au
 
 cat > $tmp.i << .
-TITLE=The First Track
-ARTIST=A Band Of Musicians
+TITLE=First Track
+ARTIST=A Band
 ALBUM=A Collection Of Songs
-DATE=2008
 TRACKNUMBER=01
-TRACKTOTAL=14
-GENRE=Music
-REPLAYGAIN_TRACK_PEAK=1.00515676
-REPLAYGAIN_TRACK_GAIN=-3.97 dB
-REPLAYGAIN_ALBUM_PEAK=1.00515676
-REPLAYGAIN_ALBUM_GAIN=-2.88 dB
 .
 
 ./sox monkey.au --comment-file $tmp.i $tmp.comments.au
@@ -77,7 +70,8 @@
 ./sox $tmp.comments.au $tmp.flac
 ./sox $tmp.flac $tmp.sf
 ./sox $tmp.sf $tmp.ogg
-./sox $tmp.ogg $tmp.au
+./sox $tmp.ogg $tmp.sndt
+./sox $tmp.sndt $tmp.au
 check_file $tmp.au $tmp.i
 
 rm -f $tmp.*
--- a/src/tests.sh
+++ b/src/tests.sh
@@ -158,7 +158,7 @@
   convertToAndFrom ima s2 u2 s3 u3 s4 u4 raw Raw dat au aiff aifc flac caf # FIXME: vox wav
 
   format1=wav1u
-  convertToAndFrom smp s1 s1X s1N s1XN sndt
+  convertToAndFrom smp s1 s1X s1N s1XN sndt sndr
   #(rate=50000; convertToAndFrom txw) || exit 1     # FIXME
   (rate=11025; convertToAndFrom hcom) || exit 1     # Fixed rates
 
--- a/src/wav.c
+++ b/src/wav.c
@@ -1324,7 +1324,7 @@
     /* If user specified opposite swap than we think, assume they are
      * asking to write a RIFX file.
      */
-    if (ft->encoding.reverse_bytes && SOX_IS_LITTLEENDIAN)
+    if (ft->encoding.reverse_bytes == SOX_IS_LITTLEENDIAN)
     {
         if (!second_header)
             sox_report("Requested to swap bytes so writing RIFX header");
--- a/src/wve.c
+++ b/src/wve.c
@@ -29,8 +29,8 @@
   char buf[sizeof(ID1)];
   uint32_t num_samples;
 
-  if (sox_readbuf(ft, buf, sizeof(buf)) != sizeof(buf) ||
-      sox_readdw(ft, &num_samples) || sox_skipbytes(ft, sizeof(ID2)))
+  if (sox_readchars(ft, buf, sizeof(buf)) || sox_readdw(ft, &num_samples) ||
+      sox_skipbytes(ft, sizeof(ID2)))
     return SOX_EOF;
   if (memcmp(ID1, buf, sizeof(buf))) {
     sox_fail_errno(ft, SOX_EHDR, "wve: can't find Psion identifier");
@@ -41,9 +41,9 @@
 
 static int write_header(sox_format_t * ft)
 {
-  return sox_writebuf(ft, ID1, sizeof(ID1)) != sizeof(ID1)
+  return sox_writechars(ft, ID1, sizeof(ID1))
       || sox_writedw(ft, ft->olength? ft->olength:ft->length)
-      || sox_writebuf(ft, ID2, sizeof(ID2)) != sizeof(ID2)? SOX_EOF:SOX_SUCCESS;
+      || sox_writechars(ft, ID2, sizeof(ID2))? SOX_EOF:SOX_SUCCESS;
 }
 
 SOX_FORMAT_HANDLER(wve)