shithub: sox

Download patch

ref: 40b45188ff81e2b2cd439222004bb2450703fefb
parent: 7a078e8c7e5eea3ba56c1a8e4cd738a47b4bd795
author: robs <robs>
date: Sun Mar 9 16:38:29 EDT 2008

a few fixes

--- a/src/8svx.c
+++ b/src/8svx.c
@@ -364,6 +364,7 @@
   static char const * const names[] = {"8svx", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 8, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Amiga audio format (a subformat of the Interchange File Format)",
     names, SOX_FILE_BIG_END|SOX_FILE_MONO|SOX_FILE_STEREO|SOX_FILE_QUAD,
     startread, read_samples, stopread,
--- a/src/adpcms.c
+++ b/src/adpcms.c
@@ -184,15 +184,28 @@
 
 sox_size_t sox_adpcm_read(sox_format_t * ft, adpcm_io_t state, sox_sample_t * buffer, sox_size_t len)
 {
-  sox_size_t n;
+  sox_size_t n = 0;
   uint8_t byte;
+  int16_t word;
 
-  for (n = 0; n < (len&~1u) && sox_readb(ft, &byte) == SOX_SUCCESS; n += 2) {
-    short word = adpcm_decode(byte >> 4, &state->encoder);
+  if (len && state->store.flag) {
+    word = adpcm_decode(state->store.byte, &state->encoder);
     *buffer++ = SOX_SIGNED_16BIT_TO_SAMPLE(word, ft->clips);
-
-    word = adpcm_decode(byte, &state->encoder);
+    state->store.flag = 0;
+    ++n;
+  }
+  while (n < len && sox_readb(ft, &byte) == SOX_SUCCESS) {
+    word = adpcm_decode(byte >> 4, &state->encoder);
     *buffer++ = SOX_SIGNED_16BIT_TO_SAMPLE(word, ft->clips);
+
+    if (++n < len) {
+      word = adpcm_decode(byte, &state->encoder);
+      *buffer++ = SOX_SIGNED_16BIT_TO_SAMPLE(word, ft->clips);
+      ++n;
+    } else {
+      state->store.byte = byte;
+      state->store.flag = 1;
+    }
   }
   return n;
 }
@@ -260,10 +273,8 @@
   }
 
   /* keep last byte across calls */
-
   state->store.byte = byte;
   state->store.flag = flag;
-
   return (count);
 }
 
@@ -286,11 +297,8 @@
 
   if (flag != 0) {
     byte <<= 4;
-    byte |= adpcm_encode(0, &state->encoder) & 0x0F;
-
     state->file.buf[state->file.count++] = byte;
   }
-
   if (state->file.count > 0)
     sox_writebuf(ft, state->file.buf, state->file.count);
 }
@@ -309,8 +317,6 @@
 int sox_adpcm_stopwrite(sox_format_t * ft, adpcm_io_t state)
 {
   sox_adpcm_flush(ft, state);
-
   free(state->file.buf);
-
   return (SOX_SUCCESS);
 }
--- a/src/aifc-fmt.c
+++ b/src/aifc-fmt.c
@@ -25,6 +25,7 @@
   static unsigned const write_encodings[] = {
     SOX_ENCODING_SIGN2, 32, 24, 16, 8, 0, 0};
   static sox_format_handler_t const sox_aifc_format = {
+    SOX_LIB_VERSION_CODE,
     "AIFF-C (not compressed, linear), defined in DAVIC 1.4 Part 9 Annex B",
     names, SOX_FILE_LOOPS | SOX_FILE_BIG_END,
     sox_aiffstartread, sox_aiffread, sox_aiffstopread,
--- a/src/aiff-fmt.c
+++ b/src/aiff-fmt.c
@@ -25,6 +25,7 @@
   static unsigned const write_encodings[] = {
     SOX_ENCODING_SIGN2, 32, 24, 16, 8, 0, 0};
   static sox_format_handler_t const sox_aiff_format = {
+    SOX_LIB_VERSION_CODE,
     "AIFF files used on Apple IIc/IIgs and SGI",
     names, SOX_FILE_LOOPS | SOX_FILE_BIG_END,
     sox_aiffstartread, sox_aiffread, sox_aiffstopread,
--- a/src/alsa.c
+++ b/src/alsa.c
@@ -666,6 +666,7 @@
     SOX_ENCODING_UNSIGNED, 16, 8, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Advanced Linux Sound Architecture device driver",
     names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
     startread, read_samples, stopread,
--- a/src/amr.h
+++ b/src/amr.h
@@ -157,6 +157,7 @@
   static sox_rate_t   const write_rates[] = {AMR_RATE, 0};
   static unsigned const write_encodings[] = {AMR_ENCODING, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "3GPP Adaptive Multi Rate lossy speech compressor",
     names, SOX_FILE_MONO,
     startread, read, stopread,
--- a/src/ao.c
+++ b/src/ao.c
@@ -127,6 +127,7 @@
   static char const * const names[] = {"ao", NULL};
   static unsigned const encodings[] = {SOX_ENCODING_SIGN2, 16, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Xiph's libao device driver",
     names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
     NULL, NULL, NULL,
--- a/src/au.c
+++ b/src/au.c
@@ -235,6 +235,7 @@
     SOX_ENCODING_FLOAT, 32, 64, 0,
     0};
   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/avr.c
+++ b/src/avr.c
@@ -280,6 +280,7 @@
     SOX_ENCODING_UNSIGNED, 16, 8, 0,
     0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Audio Visual Research format; used on the Mac",
     names,
     SOX_FILE_BIG_END|SOX_FILE_MONO|SOX_FILE_STEREO,
--- a/src/cdr.c
+++ b/src/cdr.c
@@ -40,6 +40,7 @@
   static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 16, 0, 0};
   static sox_rate_t const write_rates[] = {44100, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Red Book Compact Disc Digital Audio",
     names, SOX_FILE_BIG_END|SOX_FILE_STEREO,
     start, sox_rawread, NULL,
--- a/src/cvsd-fmt.c
+++ b/src/cvsd-fmt.c
@@ -23,6 +23,7 @@
   static char const * const names[] = {"cvsd", "cvs", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_CVSD, 1, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Headerless Continuously Variable Slope Delta modulation",
     names, SOX_FILE_MONO,
     sox_cvsdstartread, sox_cvsdread, sox_cvsdstopread,
--- a/src/dat.c
+++ b/src/dat.c
@@ -153,6 +153,7 @@
   static char const * const names[] = {"dat", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_FLOAT_TEXT, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Textual representation of the sampled audio",
     names, 0,
     sox_datstartread, sox_datread, NULL,
--- a/src/dvms-fmt.c
+++ b/src/dvms-fmt.c
@@ -23,6 +23,7 @@
   static char const * const names[] = {"dvms", "vms", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_CVSD, 1, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Continuously Variable Slope Delta modulation with header",
     names, SOX_FILE_MONO,
     sox_dvmsstartread, sox_cvsdread, sox_cvsdstopread,
--- a/src/ffmpeg.c
+++ b/src/ffmpeg.c
@@ -489,6 +489,7 @@
   static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 16, 0, 0};
 
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Pseudo format to use libffmpeg",
     names,
     SOX_FILE_NOSTDIO,
--- a/src/flac.c
+++ b/src/flac.c
@@ -519,6 +519,7 @@
   static char const * const names[] = {"flac", NULL};
   static unsigned const encodings[] = {SOX_ENCODING_FLAC, 8, 16, 24, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Free Lossless Audio CODEC compressed audio",
     names, 0,
     start_read, read_samples, stop_read,
--- a/src/gsm.c
+++ b/src/gsm.c
@@ -237,13 +237,15 @@
 SOX_FORMAT_HANDLER(gsm)
 {
   static char const * const names[] = {"gsm", NULL};
+  static sox_rate_t   const write_rates[] = {8000, 0};
   static unsigned const write_encodings[] = {SOX_ENCODING_GSM, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "GSM 06.10 (full-rate) lossy speech compression",
     names, 0,
     sox_gsmstartread, sox_gsmread, sox_gsmstopread,
     sox_gsmstartwrite, sox_gsmwrite, sox_gsmstopwrite,
-    NULL, write_encodings, NULL
+    NULL, write_encodings, write_rates
   };
   return &handler;
 }
--- a/src/hcom.c
+++ b/src/hcom.c
@@ -455,6 +455,7 @@
   static unsigned     const write_encodings[] = {
     SOX_ENCODING_HCOM, 8, 0, 0};
   static sox_format_handler_t handler   = {
+    SOX_LIB_VERSION_CODE,
     "Mac FSSD files with Huffman compression",
     names, SOX_FILE_BIG_END|SOX_FILE_MONO,
     startread, read_samples, stopread,
--- a/src/htk.c
+++ b/src/htk.c
@@ -65,6 +65,7 @@
   static char const * const names[] = {"htk", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 16, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "PCM format used for Hidden Markov Model speech processing",
     names, SOX_FILE_BIG_END | SOX_FILE_MONO | SOX_FILE_REWIND,
     start_read, sox_rawread, NULL,
--- a/src/ima-fmt.c
+++ b/src/ima-fmt.c
@@ -24,6 +24,7 @@
   static char const * const names[] = {"ima", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_IMA_ADPCM, 4, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Raw IMA ADPCM",
     names, SOX_FILE_MONO,
     sox_ima_start, sox_vox_read, sox_vox_stopread,
--- a/src/lpc10.c
+++ b/src/lpc10.c
@@ -214,6 +214,7 @@
   static sox_rate_t   const write_rates[] = {8000, 0};
   static unsigned     const write_encodings[] = {SOX_ENCODING_LPC10, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Low bandwidth, robotic sounding speech compression",
     names, SOX_FILE_MONO,
     startread, read_samples, stopread,
--- a/src/maud.c
+++ b/src/maud.c
@@ -326,6 +326,7 @@
     SOX_ENCODING_ALAW, 8, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Used with the ‘Toccata’ sound-card on the Amiga",
     names, SOX_FILE_BIG_END | SOX_FILE_MONO | SOX_FILE_STEREO,
     startread, sox_rawread, sox_rawstopread,
--- a/src/misc.c
+++ b/src/misc.c
@@ -99,7 +99,7 @@
   switch (encoding) {
     case SOX_ENCODING_HCOM:       return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 1? bits_per_sample: 0;
     case SOX_ENCODING_FLAC:       return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 3? bits_per_sample: 0;
-    case SOX_ENCODING_SIGN2:
+    case SOX_ENCODING_SIGN2:      return bits_per_sample <= 32? bits_per_sample : 0;
     case SOX_ENCODING_UNSIGNED:   return !(bits_per_sample & 7) && (bits_per_sample >> 3) - 1 < 4? bits_per_sample: 0;
 
     case SOX_ENCODING_ALAW:       return bits_per_sample == 8? 13: 0;
@@ -661,49 +661,3 @@
   }
   return fopen(identifier, mode);
 } 
-
-/* PRC detection code is here rather than in prc.c because the
-   latter is a plug-in, and prc_checkheader is called from auto.c too */
-
-/* File header. The first 4 words are fixed; the rest of the header
-   could theoretically be different, and this is the first place to
-   check with apparently invalid files.
-
-   N.B. All offsets are from start of file. */
-const char prc_header[41] = {
-  /* Header section */
-  '\x37','\x00','\x00','\x10', /* 0x00: File type (UID 1) */
-  '\x6d','\x00','\x00','\x10', /* 0x04: File kind (UID 2) */
-  '\x7e','\x00','\x00','\x10', /* 0x08: Application ID (UID 3) */
-  '\xcf','\xac','\x08','\x55', /* 0x0c: Checksum of UIDs 1-3 */
-  '\x14','\x00','\x00','\x00', /* 0x10: File offset of Section Table Section */
-  /* Section Table Section: a BListL, i.e. a list of longs preceded by
-     length byte.
-     The longs are in (ID, offset) pairs, each pair identifying a
-     section. */
-  '\x04',                      /* 0x14: List has 4 bytes, i.e. 2 pairs */
-  '\x52','\x00','\x00','\x10', /* 0x15: ID: Record Section */
-  '\x34','\x00','\x00','\x00', /* 0x19: Offset to Record Section */
-  '\x89','\x00','\x00','\x10', /* 0x1d: ID: Application ID Section */
-  '\x25','\x00','\x00','\x00', /* 0x21: Offset to Application ID Section */
-  '\x7e','\x00','\x00','\x10', /* 0x25: Application ID Section:
-                                  Record.app identifier */
-  /* Next comes the string, which can be either case. */
-};
-
-/* Format of the Record Section (offset 0x34):
-
-00 L Uncompressed data length
-04 ID a1 01 00 10 for ADPCM, 00 00 00 00 for A-law
-08 W number of times sound will be repeated (0 = played once)
-0a B Volume setting (01-05)
-0b B Always 00 (?)
-0c L Time between repeats in usec
-10 LListB (i.e. long giving number of bytes followed by bytes) Sound Data
-*/
-
-int prc_checkheader(sox_format_t * ft, char *head)
-{
-  sox_readbuf(ft, head, sizeof(prc_header));
-  return memcmp(head, prc_header, sizeof(prc_header)) == 0;
-}
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -538,6 +538,7 @@
   static unsigned const write_encodings[] = {
     SOX_ENCODING_GSM, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "MPEG Layer 3 lossy audio compression",
     names, 0,
     startread, sox_mp3read, stopread,
--- a/src/nulfile.c
+++ b/src/nulfile.c
@@ -52,6 +52,7 @@
 {
   static const char * const names[] = {"null", NULL};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     NULL,
     names, SOX_FILE_DEVICE | SOX_FILE_PHONY | SOX_FILE_NOSTDIO,
     startread, read_samples, NULL, NULL, write_samples, NULL, NULL, NULL, NULL
--- a/src/oss.c
+++ b/src/oss.c
@@ -214,6 +214,7 @@
     SOX_ENCODING_UNSIGNED, 8, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Open Sound Sytem device driver for unix-like systems",
     names, SOX_FILE_DEVICE,
     ossinit, sox_rawread, sox_rawstopread,
--- a/src/prc.c
+++ b/src/prc.c
@@ -73,9 +73,54 @@
 static int seek(sox_format_t * ft, sox_size_t offset)
 {
   prc_t p = (prc_t)ft->priv;
-  return sox_offset_seek(ft, p->data_start, offset);
+  if (ft->encoding.encoding == SOX_ENCODING_ALAW)
+    return sox_offset_seek(ft, p->data_start, offset);
+  return SOX_EOF;
 }
 
+/* File header. The first 4 words are fixed; the rest of the header
+   could theoretically be different, and this is the first place to
+   check with apparently invalid files.
+
+   N.B. All offsets are from start of file. */
+static const char prc_header[41] = {
+  /* Header section */
+  '\x37','\x00','\x00','\x10', /* 0x00: File type (UID 1) */
+  '\x6d','\x00','\x00','\x10', /* 0x04: File kind (UID 2) */
+  '\x7e','\x00','\x00','\x10', /* 0x08: Application ID (UID 3) */
+  '\xcf','\xac','\x08','\x55', /* 0x0c: Checksum of UIDs 1-3 */
+  '\x14','\x00','\x00','\x00', /* 0x10: File offset of Section Table Section */
+  /* Section Table Section: a BListL, i.e. a list of longs preceded by
+     length byte.
+     The longs are in (ID, offset) pairs, each pair identifying a
+     section. */
+  '\x04',                      /* 0x14: List has 4 bytes, i.e. 2 pairs */
+  '\x52','\x00','\x00','\x10', /* 0x15: ID: Record Section */
+  '\x34','\x00','\x00','\x00', /* 0x19: Offset to Record Section */
+  '\x89','\x00','\x00','\x10', /* 0x1d: ID: Application ID Section */
+  '\x25','\x00','\x00','\x00', /* 0x21: Offset to Application ID Section */
+  '\x7e','\x00','\x00','\x10', /* 0x25: Application ID Section:
+                                  Record.app identifier */
+  /* Next comes the string, which can be either case. */
+};
+
+/* Format of the Record Section (offset 0x34):
+
+00 L Uncompressed data length
+04 ID a1 01 00 10 for ADPCM, 00 00 00 00 for A-law
+08 W number of times sound will be repeated (0 = played once)
+0a B Volume setting (01-05)
+0b B Always 00 (?)
+0c L Time between repeats in usec
+10 LListB (i.e. long giving number of bytes followed by bytes) Sound Data
+*/
+
+static int prc_checkheader(sox_format_t * ft, char *head)
+{
+  sox_readbuf(ft, head, sizeof(prc_header));
+  return memcmp(head, prc_header, sizeof(prc_header)) == 0;
+}
+
 static int startread(sox_format_t * ft)
 {
   prc_t p = (prc_t)ft->priv;
@@ -314,15 +359,14 @@
   }
 }
 
-static sox_size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, sox_size_t samp)
+static sox_size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, sox_size_t nsamp)
 {
   prc_t p = (prc_t)ft->priv;
   /* Psion Record seems not to be able to handle frames > 800 samples */
-  samp = min(samp, 800);
-  p->nsamp += samp;
+  sox_size_t written = 0;
   sox_debug_more("length now = %d", p->nsamp);
-  if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) {
-    sox_size_t written;
+  if (ft->encoding.encoding == SOX_ENCODING_IMA_ADPCM) while (written < nsamp) {
+    sox_size_t written1, samp = min(nsamp - written, 800);
 
     write_cardinal(ft, samp);
     /* Write compressed length */
@@ -331,11 +375,15 @@
     sox_debug_more("list length %d", samp);
     sox_writedw(ft, samp);
     sox_adpcm_reset(&p->adpcm, ft->encoding.encoding);
-    written = sox_adpcm_write(ft, &p->adpcm, buf, samp);
+    written1 = sox_adpcm_write(ft, &p->adpcm, buf, samp);
+    if (written1 != samp)
+      break;
     sox_adpcm_flush(ft, &p->adpcm);
-    return written;
+    written += written1;
   } else
-    return sox_rawwrite(ft, buf, samp);
+    written = sox_rawwrite(ft, buf, nsamp);
+  p->nsamp += written;
+  return written;
 }
 
 static int stopwrite(sox_format_t * ft)
@@ -390,6 +438,7 @@
     SOX_ENCODING_IMA_ADPCM, 4, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Psion Record; used in EPOC devices (Series 5, Revo and similar)",
     names, SOX_FILE_LIT_END | SOX_FILE_MONO,
     startread, read_samples, stopread,
--- a/src/raw-fmt.c
+++ b/src/raw-fmt.c
@@ -33,6 +33,7 @@
     SOX_ENCODING_FLOAT, 64, 32, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Raw PCM, mu-law, or A-law",
     names, 0,
     raw_start, sox_rawread , NULL,
--- a/src/raw.c
+++ b/src/raw.c
@@ -53,7 +53,7 @@
     else ft->encoding.bits_per_sample = size;
   }
 
-  if (ft->mode == 'r' && default_length && ft->encoding.bits_per_sample)
+  if (!ft->length && ft->mode == 'r' && default_length && ft->encoding.bits_per_sample)
     ft->length = div_bits(sox_filelength(ft), ft->encoding.bits_per_sample);
 
   return SOX_SUCCESS;
--- a/src/raw.h
+++ b/src/raw.h
@@ -25,7 +25,7 @@
   static unsigned const write_encodings[] = { \
     SOX_ENCODING_ ## encoding, size, 0, 0}; \
   static sox_format_handler_t handler = { \
-    "Raw audio", \
+    SOX_LIB_VERSION_CODE, "Raw audio", \
     names, flags, \
     id ## _start, sox_rawread , NULL, \
     id ## _start, sox_rawwrite, NULL, \
--- a/src/sf.c
+++ b/src/sf.c
@@ -221,6 +221,7 @@
     SOX_ENCODING_FLOAT, 32, 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,
--- a/src/skelform.c
+++ b/src/skelform.c
@@ -157,17 +157,16 @@
  */
 static sox_size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, sox_size_t len)
 {
-  skelform_t UNUSED sk = (skelform_t)ft->priv;
+  skelform_t sk = (skelform_t)ft->priv;
+  sox_size_t done = 0;
 
+  (void)sk;
   switch (ft->encoding.bits_per_sample) {
   case 8:
     switch (ft->encoding.encoding) {
     case SOX_ENCODING_UNSIGNED:
-      while (len--) {
-        len = sox_writeb(ft, SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips));
-        if (len == 0)
-          break;
-      }
+      while (done < len && sox_writeb(ft, SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips)) == SOX_SUCCESS)
+        ++done;
       break;
     default:
       sox_fail("Undetected bad sample encoding in write!");
@@ -178,8 +177,7 @@
     sox_fail("Undetected bad sample size in write!");
     exit(2);
   }
-
-  return len;
+  return done;
 }
 
 static int stopwrite(sox_format_t UNUSED * ft)
@@ -213,6 +211,7 @@
    * and NULL used in place of the its name below.
    */
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "My first SoX format!",
     names, 0,
     startread, read_samples, stopread,
--- a/src/smp.c
+++ b/src/smp.c
@@ -401,6 +401,7 @@
   static char const * const names[] = {"smp", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 16, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Turtle Beach SampleVision",
     names, SOX_FILE_LOOPS | SOX_FILE_LIT_END | SOX_FILE_MONO,
     sox_smpstartread, sox_smpread, NULL,
--- a/src/sndfile.c
+++ b/src/sndfile.c
@@ -459,6 +459,7 @@
     0};
 
   static sox_format_handler_t const format = {
+    SOX_LIB_VERSION_CODE,
     "Pseudo format to use libsndfile",
     names, SOX_FILE_NOSTDIO,
     startread, read_samples, stopread,
--- a/src/sndrtool.c
+++ b/src/sndrtool.c
@@ -167,6 +167,7 @@
   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,
--- a/src/sox.c
+++ b/src/sox.c
@@ -195,7 +195,10 @@
   sox_size_t ws = ft->length / ft->signal.channels;
   (void)full;
 
-  fprintf(output, "\n%s\n\n", ft->filename);
+  fprintf(output, "\n%s:", ft->filename);
+  if (strcmp(ft->filename, "-") == 0 || (ft->handler.flags & SOX_FILE_DEVICE))
+    fprintf(output, " (%s)", ft->handler.names[0]);
+  fprintf(output, "\n\n");
 
   fprintf(output, "  Encoding: %-14s", sox_encodings_short_str[ft->encoding.encoding]);
   text = find_comment(f->ft->comments, "Comment");
@@ -248,7 +251,7 @@
   static char const * const no_yes[] = {"no", "yes"};
   FILE * const output = sox_mode == sox_soxi? stdout : stderr;
 
-  if (sox_mode == sox_play) {
+  if (sox_mode == sox_play && sox_globals.verbosity < 3) {
     play_file_info(ft, f, full);
     return;
   }
@@ -513,7 +516,8 @@
   len = sox_write(ofile->ft, ibuf, *isamp);
   output_samples += len / ofile->ft->signal.channels;
   if (len != *isamp) {
-    display_error(ofile->ft);
+    if (ofile->ft->sox_errno)
+      display_error(ofile->ft);
     return SOX_EOF;
   }
   return SOX_SUCCESS;
@@ -1575,14 +1579,27 @@
 
 static int soxi(int argc, char * const * argv)
 {
-  static char const opts[] = "rcsdbea?";
+  static char const opts[] = "rcsdbea?V::";
   soxi_t type = full;
   int opt, num_errors = 0;
 
   while ((opt = getopt(argc, argv, opts)) > 0) /* act only on last option */
-    type = 1 + (strchr(opts, opt) - opts);
+    if (opt == 'V') {
+      int i; /* sscanf silently accepts negative numbers for %u :( */
+      char dummy;     /* To check for extraneous chars in optarg. */
+      if (optarg == NULL)
+        ++sox_globals.verbosity;
+      else {
+        if (sscanf(optarg, "%i %c", &i, &dummy) != 1 || i < 0) {
+          sox_globals.verbosity = 2;
+          sox_fail("Verbosity value `%s' is not a non-negative integer", optarg);
+          exit(1);
+        }
+        sox_globals.verbosity = (unsigned)i;
+      }
+    } else type = 1 + (strchr(opts, opt) - opts);
   if (type > annotation)
-    printf("Usage: soxi [-r|-c|-s|-d|-b|-e|-a] infile1 ...\n");
+    printf("Usage: soxi [-V] [-r|-c|-s|-d|-b|-e|-a] infile1 ...\n");
   else for (; optind < argc; ++optind) {
     if (sox_is_playlist(argv[optind]))
       num_errors += (sox_parse_playlist((sox_playlist_callback_t)soxi1, &type, argv[optind]) != SOX_SUCCESS);
--- a/src/sox.h
+++ b/src/sox.h
@@ -30,7 +30,7 @@
  * Please do not count on these numbers being in sync.
  * The following is at 14.0.1
  */
-#define SOX_LIB_VERSION_CODE 0x0e0001
+#define SOX_LIB_VERSION_CODE 0x0e0100
 #define SOX_LIB_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
 
 const char *sox_version(void);   /* Returns version number */
@@ -291,18 +291,19 @@
 typedef struct sox_format sox_format_t;
 
 typedef struct {
-    char         const * description;
-    char         const * const * names;
-    unsigned int flags;
-    int          (*startread)(sox_format_t * ft);
-    sox_size_t   (*read)(sox_format_t * ft, sox_sample_t *buf, sox_size_t len);
-    int          (*stopread)(sox_format_t * ft);
-    int          (*startwrite)(sox_format_t * ft);
-    sox_size_t   (*write)(sox_format_t * ft, const sox_sample_t *buf, sox_size_t len);
-    int          (*stopwrite)(sox_format_t * ft);
-    int          (*seek)(sox_format_t * ft, sox_size_t offset);
-    unsigned     const * write_formats;
-    sox_rate_t   const * write_rates;
+  unsigned     sox_lib_version_code; /* Checked on load; must be 1st in struct*/
+  char         const * description;
+  char         const * const * names;
+  unsigned int flags;
+  int          (*startread)(sox_format_t * ft);
+  sox_size_t   (*read)(sox_format_t * ft, sox_sample_t *buf, sox_size_t len);
+  int          (*stopread)(sox_format_t * ft);
+  int          (*startwrite)(sox_format_t * ft);
+  sox_size_t   (*write)(sox_format_t * ft, const sox_sample_t *buf, sox_size_t len);
+  int          (*stopwrite)(sox_format_t * ft);
+  int          (*seek)(sox_format_t * ft, sox_size_t offset);
+  unsigned     const * write_formats;
+  sox_rate_t   const * write_rates;
 } sox_format_handler_t;
 
 /*
--- a/src/sox_i.h
+++ b/src/sox_i.h
@@ -313,10 +313,6 @@
 sox_format_handler_t const * sox_##name##_format_fn(void)
 #define div_bits(size, bits) (off_t)((double)(size) * 8 / bits)
 
-/* Psion record header check, defined in misc.c and used in prc.c and auto.c */
-extern const char prc_header[41];
-int prc_checkheader(sox_format_t * ft, char *head);
-
 typedef const sox_format_handler_t *(*sox_format_fn_t)(void);
 
 typedef struct {
--- a/src/soxio.c
+++ b/src/soxio.c
@@ -71,10 +71,11 @@
     if (ret > 0 && ret < MAX_NAME_LEN) {
       union {sox_format_fn_t fn; lt_ptr ptr;} ltptr;
       ltptr.ptr = lt_dlsym(lth, fnname);
-      sox_format_fns[sox_formats].fn = ltptr.fn;
       sox_debug("opening format plugin `%s': library %p, entry point %p\n", fnname, (void *)lth, ltptr.ptr);
-      if (sox_format_fns[sox_formats].fn)
+      if (ltptr.fn && (ltptr.fn()->sox_lib_version_code & ~255) == (SOX_LIB_VERSION_CODE & ~255)){
+        sox_format_fns[sox_formats].fn = ltptr.fn;
         sox_formats++;
+      }
     }
   }
 
@@ -208,7 +209,7 @@
   MAGIC(smp   , 0, 0, ""     , 0, 17, "SOUND SAMPLE DATA")
   MAGIC(wve   , 0, 0, ""     , 0, 15, "ALawSoundFile**")
   MAGIC(amr-wb, 0, 0, ""     , 0,  9, "#!AMR-WB\n")
-  MAGIC(prc   , 0, 0, ""     , 0,  8, prc_header)
+  MAGIC(prc   , 0, 0, ""     , 0,  8, "\x37\x00\x00\x10\x6d\x00\x00\x10")
   MAGIC(sph   , 0, 0, ""     , 0,  7, "NIST_1A")
   MAGIC(amr-nb, 0, 0, ""     , 0,  6, "#!AMR\n")
   MAGIC(txw   , 0, 0, ""     , 0,  6, "LM8953")
@@ -899,4 +900,3 @@
   free(dirname);
   return result;
 }
-
--- a/src/sphere.c
+++ b/src/sphere.c
@@ -249,6 +249,7 @@
     SOX_ENCODING_ULAW, 8, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "SPeech HEader Resources; defined by NIST",
     names, 0,
     startread, sox_rawread, sox_rawstopread,
--- a/src/sunaudio.c
+++ b/src/sunaudio.c
@@ -316,6 +316,7 @@
     SOX_ENCODING_SIGN2, 8, 16, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Sun audio device driver",
     names, SOX_FILE_DEVICE,
     sox_sunstartread, sox_rawread, sox_rawstopread,
--- a/src/tests.sh
+++ b/src/tests.sh
@@ -159,6 +159,7 @@
 
   format1=wav1u
   convertToAndFrom smp s1 s1X s1N s1XN sndt
+  #(rate=50000; convertToAndFrom txw) || exit 1     # FIXME
   (rate=11025; convertToAndFrom hcom) || exit 1     # Fixed rates
 
   format1=wve
--- a/src/tx16w.c
+++ b/src/tx16w.c
@@ -41,8 +41,12 @@
 
 /* Private data for TX16 file */
 typedef struct txwstuff {
-        sox_size_t rest;                 /* bytes remaining in sample file */
-} *txw_t;
+  sox_size_t   samples_out;
+  sox_size_t   bytes_out;
+  sox_size_t   rest;                 /* bytes remaining in sample file */
+  sox_sample_t odd;
+  sox_bool     odd_flag;
+} * txw_t;
 
 struct WaveHeader_ {
   char filetype[6]; /* = "LM8953", */
@@ -54,15 +58,11 @@
     atc_length[3],   /* I'll get to this... */
     rpt_length[3],
     unused[2];       /* set these to null, to be on the safe side */
-} ;
+};
 
 static const unsigned char magic1[4] = {0, 0x06, 0x10, 0xF6};
 static const unsigned char magic2[4] = {0, 0x52, 0x00, 0x52};
 
-/* FIXME SJB: dangerous static variables */
-static sox_size_t tx16w_len=0;
-static sox_size_t writedone=0;
-
 /*
  * Do anything required before you start reading samples.
  * Read file header. 
@@ -131,13 +131,13 @@
 
     switch( sample_rate ) {
         case 1:
-            ft->signal.rate = 33333;
+            ft->signal.rate = 1e5 / 3;
             break;
         case 2:
-            ft->signal.rate = 50000;
+            ft->signal.rate = 1e5 / 2;
             break;
         case 3:
-            ft->signal.rate = 16667;
+            ft->signal.rate = 1e5 / 6;
             break;
         default:
             blewIt = 1;
@@ -145,31 +145,31 @@
                 case 0x06:
                     if ( (gunk[5] & 0xFE) == 0x52 ) {
                         blewIt = 0;
-                        ft->signal.rate = 33333;
+                        ft->signal.rate = 1e5 / 3;
                     }
                     break;
                 case 0x10:
                     if ( (gunk[5] & 0xFE) == 0x00 ) {
                         blewIt = 0;
-                        ft->signal.rate = 50000;
+                        ft->signal.rate = 1e5 / 2;
                     }
                     break;
                 case 0xF6:
                     if ( (gunk[5] & 0xFE) == 0x52 ) {
                         blewIt = 0;
-                        ft->signal.rate = 16667;
+                        ft->signal.rate = 1e5 / 6;
                     }
                     break;
             }
             if ( blewIt ) {
                 sox_debug("Invalid sample rate identifier found %d", (int)sample_rate);
-                ft->signal.rate = 33333;
+                ft->signal.rate = 1e5 / 3;
             }
     }
     sox_debug("Sample rate = %g", ft->signal.rate);
 
     ft->signal.channels = 1 ; /* not sure about stereo sample data yet ??? */
-    ft->encoding.bits_per_sample = 16; /* this is close enough */
+    ft->encoding.bits_per_sample = 12;
     ft->encoding.encoding = SOX_ENCODING_SIGN2;
 
     return(SOX_SUCCESS);
@@ -234,6 +234,7 @@
 
 static int startwrite(sox_format_t * ft)
 {
+  txw_t sk = (txw_t) ft->priv;
     struct WaveHeader_ WH;
 
     sox_debug("tx16w selected output");
@@ -240,10 +241,6 @@
 
     memset(&WH, 0, sizeof(struct WaveHeader_));
 
-    if (ft->signal.channels != 1)
-        sox_report("tx16w is overriding output format to 1 channel.");
-    ft->signal.channels = 1 ; /* not sure about stereo sample data yet ??? */
-
     /* If you have to seek around the output file */
     if (! ft->seekable)
     {
@@ -255,40 +252,54 @@
        at end of processing, since byte count is needed */
 
     sox_writebuf(ft, &WH, 32);
-    writedone = 32;
+    sk->bytes_out = 32;
     return(SOX_SUCCESS);
 }
 
-static sox_size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, sox_size_t len)
+static sox_size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, sox_size_t len0)
 {
-  sox_size_t i;
-  sox_sample_t w1,w2;
+  txw_t sk = (txw_t) ft->priv;
+  sox_size_t last_i, i = 0, len = min(len0, TXMAXLEN - sk->samples_out);
+  sox_sample_t w1, w2;
 
-  tx16w_len += len;
-  if (tx16w_len > TXMAXLEN) {
-    sox_fail_errno(ft, SOX_EOF, "Audio too long for TX16W file");
-    return 0;
-  }
-  for (i=0;i<len;i+=2) {
-      w1 =  *buf++ >> 20;
-      if (i+1==len)
-          w2 = 0;
-      else {
-          w2 =  *buf++ >> 20;
+  while (i < len) {
+    last_i = i;
+    if (sk->odd_flag) {
+      w1 = sk->odd;
+      sk->odd_flag = sox_false;
+    }
+    else w1 = *buf++ >> 20, ++i;
+
+    if (i < len) {
+      w2 = *buf++ >> 20, ++i;
+      if (sox_writesb(ft, (w1 >> 4) & 0xFF) ||
+          sox_writesb(ft, (((w1 & 0x0F) << 4) | (w2 & 0x0F)) & 0xFF) ||
+          sox_writesb(ft, (w2 >> 4) & 0xFF)) {
+        i = last_i;
+        break;
       }
-      sox_writesb(ft, (w1 >> 4) & 0xFF);
-      sox_writesb(ft, (((w1 & 0x0F) << 4) | (w2 & 0x0F)) & 0xFF);
-      sox_writesb(ft, (w2 >> 4) & 0xFF);
-      writedone += 3;
+      sk->samples_out += 2;
+      sk->bytes_out += 3;
+    }
+    else {
+      sk->odd = w1;
+      sk->odd_flag = sox_true;
+    }
   }
-  return(len);
+  return i;
 }
 
 static int stopwrite(sox_format_t * ft)
 {
+  txw_t sk = (txw_t) ft->priv;
     struct WaveHeader_ WH;
     int AttackLength, LoopLength, i;
 
+    if (sk->odd_flag) {
+      sox_sample_t pad = 0;
+      write_samples(ft, &pad, 1);
+    }
+
     /* All samples are already written out. */
     /* If file header needs fixing up, for example it needs the */
     /* the number of samples in a field, seek back and write them here. */
@@ -310,39 +321,39 @@
     else if (ft->signal.rate < 41000) WH.sample_rate = 1;
     else                            WH.sample_rate = 2;
 
-    if (tx16w_len >= TXMAXLEN) {
+    if (sk->samples_out >= TXMAXLEN) {
         sox_warn("Sound too large for TX16W. Truncating, Loop Off");
         AttackLength       = TXMAXLEN/2;
         LoopLength         = TXMAXLEN/2;
     }
-    else if (tx16w_len >=TXMAXLEN/2) {
+    else if (sk->samples_out >=TXMAXLEN/2) {
         AttackLength       = TXMAXLEN/2;
-        LoopLength         = tx16w_len - TXMAXLEN/2;
+        LoopLength         = sk->samples_out - TXMAXLEN/2;
         if (LoopLength < 0x40) {
             LoopLength   +=0x40;
             AttackLength -= 0x40;
         }
     }    
-    else if (tx16w_len >= 0x80) {
-        AttackLength                       = tx16w_len -0x40;
+    else if (sk->samples_out >= 0x80) {
+        AttackLength                       = sk->samples_out -0x40;
         LoopLength                         = 0x40;
     }
     else {
         AttackLength                       = 0x40;
         LoopLength                         = 0x40;
-        for(i=tx16w_len;i<0x80;i++) {
+        for(i=sk->samples_out;i<0x80;i++) {
             sox_writeb(ft, 0);
             sox_writeb(ft, 0);
             sox_writeb(ft, 0);
-            writedone += 3;
+            sk->bytes_out += 3;
         }
     }
 
     /* Fill up to 256 byte blocks; the TX16W seems to like that */
 
-    while ((writedone % 0x100) != 0) {
+    while ((sk->bytes_out % 0x100) != 0) {
         sox_writeb(ft, 0);
-        writedone++;
+        sk->bytes_out++;
     }
 
     WH.atc_length[0] = 0xFF & AttackLength;
@@ -364,13 +375,15 @@
 SOX_FORMAT_HANDLER(txw)
 {
   static char const * const names[] = {"txw", NULL};
-  static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 16, 0, 0};
+  static sox_rate_t   const write_rates[] = {1e5/6, 1e5/3, 1e5/2, 0};
+  static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 12, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Yamaha TX-16W sampler",
-    names, 0,
+    names, SOX_FILE_MONO,
     startread, read_samples, NULL,
     startwrite, write_samples, stopwrite,
-    NULL, write_encodings, NULL
+    NULL, write_encodings, write_rates
   };
   return &handler;
 }
--- a/src/voc.c
+++ b/src/voc.c
@@ -789,6 +789,7 @@
     0
   };
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Creative Technology Sound Blaster format",
     names, SOX_FILE_LIT_END | SOX_FILE_MONO | SOX_FILE_STEREO,
     startread, read_samples, NULL,
--- a/src/vorbis.c
+++ b/src/vorbis.c
@@ -397,6 +397,7 @@
   static const char *names[] = {"vorbis", "ogg", NULL};
   static unsigned encodings[] = {SOX_ENCODING_VORBIS, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Xiph's ogg-vorbis lossy compression",
     names, 0,
     startread, read_samples, stopread,
--- a/src/vox-fmt.c
+++ b/src/vox-fmt.c
@@ -24,6 +24,7 @@
   static char const * const names[] = {"vox", NULL};
   static unsigned const write_encodings[] = {SOX_ENCODING_OKI_ADPCM, 4, 0, 0};
   static sox_format_handler_t handler = {
+    SOX_LIB_VERSION_CODE,
     "Raw OKI/Dialogic ADPCM",
     names, SOX_FILE_MONO,
     sox_vox_start, sox_vox_read, sox_vox_stopread,
--- a/src/wav.c
+++ b/src/wav.c
@@ -1614,6 +1614,7 @@
     SOX_ENCODING_FLOAT, 32, 0,
     0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Microsoft audio format",
     names, SOX_FILE_LIT_END,
     startread, read_samples, stopread,
--- a/src/wve.c
+++ b/src/wve.c
@@ -52,6 +52,7 @@
   static sox_rate_t   const write_rates[] = {8000, 0};
   static unsigned     const write_encodings[] = {SOX_ENCODING_ALAW, 8, 0, 0};
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "Psion 3 audio format",
     names, SOX_FILE_BIG_END | SOX_FILE_MONO | SOX_FILE_REWIND,
     start_read, sox_rawread, NULL,
--- a/src/xa.c
+++ b/src/xa.c
@@ -284,6 +284,7 @@
 {
   static char const * const names[] = {"xa", NULL };
   static sox_format_handler_t const handler = {
+    SOX_LIB_VERSION_CODE,
     "16-bit ADPCM audio files used by Maxis games",
     names, SOX_FILE_LIT_END,
     startread, read_samples, stopread,