shithub: sox

Download patch

ref: bd9940ebc5929cf32aafba1f118525fdaf393cab
parent: 84145f47ba249412f48ec7618ffc55f09ac72af9
author: robs <robs>
date: Wed Mar 5 12:12:42 EST 2008

Added some VOC ADPCMs

--- a/AUTHORS
+++ b/AUTHORS
@@ -15,8 +15,8 @@
 		format support, libao playback, Secret Rabbit Code
 		resampling; many fixes and much cleanup.
 	Rob Sykes		robs@users.sourceforge.net
-                Formats: M3U, PLS, FLAC, AMR, HTK, 24bit support for popular
-                formats.
+                Formats: M3U, PLS, FLAC, AMR, HTK, VOC ADPCM,
+                24bit support for popular formats.
 		Effects: key, tempo, pad, bass, treble, new reverb, new
 		flanger, soft-knee companding, speed via resampling, filters
 		makeover inc. gnuplot & octave plotting, splice, remix, norm;
--- a/ChangeLog
+++ b/ChangeLog
@@ -36,6 +36,7 @@
   o Au/snd: added support for 32-bit integer and 64-bit float PCM
     encoding/decoding; display name of unsupported encoding.  (robs)
   o Can now write .amb (.wav variant) files [FR 1902232].  (robs)
+  o Can now read 2,3,4 bit ADPCM .voc files.  (robs)
 
 Effects:
 
--- a/soxformat.7
+++ b/soxformat.7
@@ -533,8 +533,8 @@
 and sample data with a new sample rate is rejected.
 Silence with a different sample rate is generated appropriately.
 On output, silence is not detected, nor are impossible sample rates.
-Note, this version now supports playing VOC files with multiple
-blocks and supports playing files containing \(*m-law and A-law samples.
+SoX supports reading (but not writing) VOC files with multiple
+blocks, and files containing \(*m-law, A-law, and 2/3/4-bit ADPCM samples.
 .TP
 .B .vorbis
 See
--- a/src/adpcms.c
+++ b/src/adpcms.c
@@ -14,12 +14,12 @@
  * Fifth Floor, 51 Franklin Street, Boston, MA 02111-1301, USA.
  */
 
-/* ADPCM CODECs: IMA, OKI.   (c) 2007 robs@users.sourceforge.net */
+/* ADPCM CODECs: IMA, OKI, CL.   (c) 2007-8 robs@users.sourceforge.net */
 
 #include "sox_i.h"
 #include "adpcms.h"
 
-static int const ima_steps[89] = { /* ~16-bit precision */
+static int const ima_steps[89] = { /* ~16-bit precision; 4 bit code */
   7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
   50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230,
   253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963,
@@ -26,62 +26,78 @@
   1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
   3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442,
   11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
-  32767 };
+  32767};
 
-static int const oki_steps[49] = { /* ~12-bit precision */
+static int const oki_steps[49] = { /* ~12-bit precision; 4 bit code */
   256, 272, 304, 336, 368, 400, 448, 496, 544, 592, 656, 720, 800, 880, 960,
   1056, 1168, 1280, 1408, 1552, 1712, 1888, 2080, 2288, 2512, 2768, 3040, 3344,
   3680, 4048, 4464, 4912, 5392, 5936, 6528, 7184, 7904, 8704, 9568, 10528,
-  11584, 12736, 14016, 15408, 16960, 18656, 20512, 22576, 24832 };
+  11584, 12736, 14016, 15408, 16960, 18656, 20512, 22576, 24832};
 
 static int const step_changes[8] = {-1, -1, -1, -1, 2, 4, 6, 8};
 
-static void adpcm_init(adpcm_t state, int type)
+/* Creative Labs ~8 bit precision; 4, 3, & 2 bit codes: */
+static int const cl4_steps[4] = {0x100, 0x200, 0x400, 0x800};
+static int const cl4_changes[8] = {-1, 0, 0, 0, 0, 1, 1, 1};
+
+static int const cl3_steps[5] = {0x100, 0x200, 0x400, 0x800, 0xA00};
+static int const cl3_changes[4] = {-1, 0, 0, 1};
+
+static int const cl2_steps[6] = {0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000};
+static int const cl2_changes[2] = {-1, 1};
+
+static adpcm_setup_t const setup_table[] = {
+  {88, 8, 2, ima_steps, step_changes, ~0},
+  {48, 8, 2, oki_steps, step_changes, ~15},
+  { 3, 8, 0, cl4_steps, cl4_changes , ~255},
+  { 4, 4, 0, cl3_steps, cl3_changes , ~255},
+  { 5, 2, 0, cl2_steps, cl2_changes , ~255},
+};
+
+void adpcm_init(adpcm_t * p, int type, int first_sample)
 {
-  state->last_output = 0;
-  state->step_index = 0;
-  state->max_step_index = type? 48 : 88;
-  state->steps = type? oki_steps : ima_steps;
-  state->mask = type? ~15 : ~0;
-  state->errors = 0;
+  p->setup = setup_table[type];
+  p->last_output = first_sample;
+  p->step_index = 0;
+  p->errors = 0;
 }
 
 #define min_sample -0x8000
 #define max_sample 0x7fff
 
-static int adpcm_decode(int code, adpcm_t state)
+int adpcm_decode(int code, adpcm_t * p)
 {
-  int s = ((code & 7) << 1) | 1;
-  s = ((state->steps[state->step_index] * s) >> 3) & state->mask;
-  if (code & 8)
+  int s = ((code & (p->setup.sign - 1)) << 1) | 1;
+  s = ((p->setup.steps[p->step_index] * s) >> (p->setup.shift + 1)) & p->setup.mask;
+  if (code & p->setup.sign)
     s = -s;
-  s += state->last_output;
+  s += p->last_output;
   if (s < min_sample || s > max_sample) {
-    int grace = (state->steps[state->step_index] >> 3) & state->mask;
+    int grace = (p->setup.steps[p->step_index] >> (p->setup.shift + 1)) & p->setup.mask;
     if (s < min_sample - grace || s > max_sample + grace) {
       sox_debug_most("code=%i step=%i grace=%i s=%i", 
-          code & 15, state->steps[state->step_index], grace, s);
-      state->errors++;
+          code & (2 * p->setup.sign - 1), p->setup.steps[p->step_index], grace, s);
+      p->errors++;
     }
     s = s < min_sample? min_sample : max_sample;
   }
-  state->step_index += step_changes[code & 0x07];
-  state->step_index = range_limit(state->step_index, 0, state->max_step_index);
-  return state->last_output = s;
+  p->step_index += p->setup.changes[code & (p->setup.sign - 1)];
+  p->step_index = range_limit(p->step_index, 0, p->setup.max_step_index);
+  return p->last_output = s;
 }
 
-static int adpcm_encode(int sample, adpcm_t state)
+int adpcm_encode(int sample, adpcm_t * p)
 {
-  int delta = sample - state->last_output;
+  int delta = sample - p->last_output;
   int sign = 0;
   int code;
   if (delta < 0) {
-    sign = 0x08;
+    sign = p->setup.sign;
     delta = -delta;
   }
-  code = 4 * delta / state->steps[state->step_index];
-  code = sign | min(code, 7);
-  adpcm_decode(code, state); /* Update encoder state */
+  code = (delta << p->setup.shift) / p->setup.steps[p->step_index];
+  code = sign | min(code, p->setup.sign - 1);
+  adpcm_decode(code, p); /* Update encoder state */
   return code;
 }
 
@@ -111,7 +127,7 @@
   state->store.byte = 0;
   state->store.flag = 0;
 
-  adpcm_init(&state->encoder, (type == SOX_ENCODING_OKI_ADPCM) ? 1 : 0);
+  adpcm_init(&state->encoder, (type == SOX_ENCODING_OKI_ADPCM) ? 1 : 0, 0);
 }
 
 /******************************************************************************
--- a/src/adpcms.h
+++ b/src/adpcms.h
@@ -16,18 +16,28 @@
 
 /* ADPCM CODECs: IMA, OKI.   (c) 2007 robs@users.sourceforge.net */
 
-typedef struct adpcm_struct
-{
-  int last_output;
-  int step_index;
+typedef struct {
   int max_step_index;
+  int sign;
+  int shift;
   int const * steps;
+  int const * changes;
   int mask;
+} adpcm_setup_t;
+
+typedef struct {
+  adpcm_setup_t setup;
+  int last_output;
+  int step_index;
   int errors;
-} * adpcm_t;
+} adpcm_t;
 
+void adpcm_init(adpcm_t * p, int type, int first_sample);
+int adpcm_decode(int code, adpcm_t * p);
+int adpcm_encode(int sample, adpcm_t * p);
+
 typedef struct adpcm_io {
-  struct adpcm_struct encoder;
+  adpcm_t encoder;
   struct {
     uint8_t byte;               /* write store */
     uint8_t flag;
--- a/src/misc.c
+++ b/src/misc.c
@@ -42,6 +42,8 @@
   "A-law",
   "G.721 ADPCM",
   "G.723 ADPCM",
+  "CL ADPCM (from 8-bit)",
+  "CL ADPCM (from 16-bit)",
   "MS ADPCM",
   "IMA ADPCM",
   "OKI ADPCM",
@@ -76,6 +78,8 @@
     case SOX_ENCODING_ALAW:       return bits_per_sample == 8? 13: 0;
     case SOX_ENCODING_ULAW:       return bits_per_sample == 8? 14: 0;
 
+    case SOX_ENCODING_CL_ADPCM:   return bits_per_sample? 8: 0;
+    case SOX_ENCODING_CL_ADPCM16: return bits_per_sample == 4? 13: 0;
     case SOX_ENCODING_MS_ADPCM:   return bits_per_sample == 4? 14: 0;
     case SOX_ENCODING_IMA_ADPCM:  return bits_per_sample == 4? 13: 0;
     case SOX_ENCODING_OKI_ADPCM:  return bits_per_sample == 4? 12: 0;
--- a/src/sox.h
+++ b/src/sox.h
@@ -188,6 +188,8 @@
   SOX_ENCODING_ALAW      , /* A-law signed logs: non-US telephony */
   SOX_ENCODING_G721      , /* G.721 4-bit ADPCM */
   SOX_ENCODING_G723      , /* G.723 3 or 5 bit ADPCM */
+  SOX_ENCODING_CL_ADPCM  , /* Creative Labs 8 --> 2,3,4 bit Compressed PCM */
+  SOX_ENCODING_CL_ADPCM16, /* Creative Labs 16 --> 4 bit Compressed PCM */
   SOX_ENCODING_MS_ADPCM  , /* Microsoft Compressed PCM */
   SOX_ENCODING_IMA_ADPCM , /* IMA Compressed PCM */
   SOX_ENCODING_OKI_ADPCM , /* Dialogic/OKI Compressed PCM */
--- a/src/voc.c
+++ b/src/voc.c
@@ -20,7 +20,7 @@
  * For sox-12-17 by Annonymous (see notes ANN)
  * Added comments and notes for each procedure.
  * Fixed so this now works with pipes, input does not have to
- * be seekable anymore (in sox_vocstartread() )
+ * be seekable anymore (in startread() )
  * Added support for uLAW and aLaw (aLaw not tested).
  * Fixed support of multi-part VOC files, and files with
  * block 9 but no audio in the block....
@@ -159,21 +159,24 @@
 
 #include "sox_i.h"
 #include "g711.h"
+#include "adpcms.h"
+#include <assert.h>
 #include <string.h>
 
 /* Private data for VOC file */
 typedef struct vocstuff {
-    long           rest;        /* bytes remaining in current block */
-    long           rate;        /* rate code (byte) of this chunk */
-    int            silent;      /* sound or silence? */
-    long           srate;       /* rate code (byte) of silence */
-    sox_size_t     blockseek;   /* start of current output block */
-    long           samples;     /* number of samples output */
-    uint16_t       format;      /* VOC audio format */
-    int            size;        /* word length of data */
-    unsigned char  channels;    /* number of sound channels */
-    long           total_size;  /* total size of all audio in file */
-    int            extended;    /* Has an extended block been read? */
+  long block_remaining;         /* bytes remaining in current block */
+  long rate;                    /* rate code (byte) of this chunk */
+  int silent;                   /* sound or silence? */
+  long srate;                   /* rate code (byte) of silence */
+  sox_size_t blockseek;         /* start of current output block */
+  long samples;                 /* number of samples output */
+  uint16_t format;              /* VOC audio format */
+  int size;                     /* word length of data */
+  unsigned char channels;       /* number of sound channels */
+  long total_size;              /* total size of all audio in file */
+  int extended;                 /* Has an extended block been read? */
+  adpcm_t adpcm;
 } *vs_t;
 
 #define VOC_TERM        0
@@ -188,14 +191,14 @@
 #define VOC_DATA_16     9
 
 /* ANN:  Format encoding types */
-#define VOC_FMT_LIN8U          0   /* 8 bit unsigned linear PCM */
-#define VOC_FMT_CRLADPCM4      1   /* Creative 8-bit to 4-bit ADPCM */
-#define VOC_FMT_CRLADPCM3      2   /* Creative 8-bit to 3-bit ADPCM */
-#define VOC_FMT_CRLADPCM2      3   /* Creative 8-bit to 2-bit ADPCM */
-#define VOC_FMT_LIN16          4   /* 16-bit signed PCM */
-#define VOC_FMT_ALAW           6   /* CCITT a-Law 8-bit PCM */
-#define VOC_FMT_MU255          7   /* CCITT u-Law 8-bit PCM */
-#define VOC_FMT_CRLADPCM4A 0x200   /* Creative 16-bit to 4-bit ADPCM */
+#define VOC_FMT_LIN8U          0        /* 8 bit unsigned linear PCM */
+#define VOC_FMT_CRLADPCM4      1        /* Creative 8-bit to 4-bit ADPCM */
+#define VOC_FMT_CRLADPCM3      2        /* Creative 8-bit to 3-bit ADPCM */
+#define VOC_FMT_CRLADPCM2      3        /* Creative 8-bit to 2-bit ADPCM */
+#define VOC_FMT_LIN16          4        /* 16-bit signed PCM */
+#define VOC_FMT_ALAW           6        /* CCITT a-Law 8-bit PCM */
+#define VOC_FMT_MU255          7        /* CCITT u-Law 8-bit PCM */
+#define VOC_FMT_CRLADPCM4A 0x200        /* Creative 16-bit to 4-bit ADPCM */
 
 /* Prototypes for internal functions */
 static int getblock(sox_format_t *);
@@ -206,196 +209,237 @@
 #define SOX_ULAW_BYTE_TO_SAMPLE(d) ((sox_sample_t)(sox_ulaw2linear16(d)) << 16)
 
 /* public VOC functions for SOX */
+
 /*-----------------------------------------------------------------
- * sox_vocstartread() -- start reading a VOC file
+ * startread() -- start reading a VOC file
  *-----------------------------------------------------------------*/
-static int sox_vocstartread(sox_format_t * ft)
+static int startread(sox_format_t * ft)
 {
-        int rtn = SOX_SUCCESS;
-        char header[20];
-        vs_t v = (vs_t) ft->priv;
-        unsigned short sbseek;
-        int rc;
-        int ii;  /* for getting rid of lseek */
-        unsigned char uc;
+  int rtn = SOX_SUCCESS;
+  char header[20];
+  vs_t v = (vs_t) ft->priv;
+  unsigned short sbseek;
+  int rc;
+  int ii;                       /* for getting rid of lseek */
+  unsigned char uc;
 
-        if (sox_readbuf(ft, header, 20) != 20)
-        {
-                sox_fail_errno(ft,SOX_EHDR,"unexpected EOF in VOC header");
-                return(SOX_EOF);
-        }
-        if (strncmp(header, "Creative Voice File\032", 19))
-        {
-                sox_fail_errno(ft,SOX_EHDR,"VOC file header incorrect");
-                return(SOX_EOF);
-        }
+  if (sox_readbuf(ft, header, 20) != 20) {
+    sox_fail_errno(ft, SOX_EHDR, "unexpected EOF in VOC header");
+    return (SOX_EOF);
+  }
+  if (strncmp(header, "Creative Voice File\032", 19)) {
+    sox_fail_errno(ft, SOX_EHDR, "VOC file header incorrect");
+    return (SOX_EOF);
+  }
 
-        /* read the offset to data, from start of file */
-        /* after this read we have read 20 bytes of header + 2 */
-        sox_readw(ft, &sbseek);
+  /* read the offset to data, from start of file */
+  /* after this read we have read 20 bytes of header + 2 */
+  sox_readw(ft, &sbseek);
 
-        /* ANN:  read to skip the header, instead of lseek */
-        /* this should allow use with pipes.... */
-        for (ii=22; ii<sbseek; ii++)
-            sox_readb(ft, &uc);
+  /* ANN:  read to skip the header, instead of lseek */
+  /* this should allow use with pipes.... */
+  for (ii = 22; ii < sbseek; ii++)
+    sox_readb(ft, &uc);
 
-        v->rate = -1;
-        v->rest = 0;
-        v->total_size = 0;  /* ANN added */
-        v->extended = 0;
-        v->format = VOC_FMT_LIN8U;
+  v->rate = -1;
+  v->block_remaining = 0;
+  v->total_size = 0;    /* ANN added */
+  v->extended = 0;
 
-        /* read until we get the format information.... */
-        rc = getblock(ft);
-        if (rc)
-            return rc;
+  /* read until we get the format information.... */
+  rc = getblock(ft);
+  if (rc)
+    return rc;
 
-        /* get rate of data */
-        if (v->rate == -1)
-        {
-                sox_fail_errno(ft,SOX_EOF,"Input .voc file had no sound!");
-                return(SOX_EOF);
-        }
+  /* get rate of data */
+  if (v->rate == -1) {
+    sox_fail_errno(ft, SOX_EOF, "Input .voc file had no sound!");
+    return (SOX_EOF);
+  }
 
-        /* setup word length of data */
-        ft->encoding.bits_per_sample = v->size;
+  /* setup word length of data */
 
-        /* ANN:  Check VOC format and map to the proper libSoX format value */
-        switch (v->format) {
-        case VOC_FMT_LIN8U:      /*     0    8 bit unsigned linear PCM */
-            ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
-            break;
-        case VOC_FMT_CRLADPCM4:  /*     1    Creative 8-bit to 4-bit ADPCM */
-            sox_fail ("Unsupported VOC format CRLADPCM4 %d", v->format);
-            rtn=SOX_EOF;
-            break;
-        case VOC_FMT_CRLADPCM3:  /*     2    Creative 8-bit to 3-bit ADPCM */
-            sox_fail ("Unsupported VOC format CRLADPCM3 %d", v->format);
-            rtn=SOX_EOF;
-            break;
-        case VOC_FMT_CRLADPCM2:  /*     3    Creative 8-bit to 2-bit ADPCM */
-            sox_fail ("Unsupported VOC format CRLADPCM2 %d", v->format);
-            rtn=SOX_EOF;
-            break;
-        case VOC_FMT_LIN16:      /*     4    16-bit signed PCM */
-            ft->encoding.encoding = SOX_ENCODING_SIGN2;
-            break;
-        case VOC_FMT_ALAW:       /*     6    CCITT a-Law 8-bit PCM */
-            ft->encoding.encoding = SOX_ENCODING_ALAW;
-            break;
-        case VOC_FMT_MU255:      /*     7    CCITT u-Law 8-bit PCM */
-            ft->encoding.encoding = SOX_ENCODING_ULAW;
-            break;
-        case VOC_FMT_CRLADPCM4A: /*0x200    Creative 16-bit to 4-bit ADPCM */
-            sox_fail ("Unsupported VOC format CRLADPCM4A %d", v->format);
-            rtn=SOX_EOF;
-            break;
-        default:
-            sox_fail ("Unknown VOC format %d", v->format);
-            rtn=SOX_EOF;
-            break;
-        }
+  /* ANN:  Check VOC format and map to the proper libSoX format value */
+  switch (v->format) {
+    case VOC_FMT_LIN8U:        /*     0    8 bit unsigned linear PCM */
+      ft->encoding.encoding = SOX_ENCODING_UNSIGNED;
+      v->size = 8;
+      break;
+    case VOC_FMT_CRLADPCM4:    /*     1    Creative 8-bit to 4-bit ADPCM */
+      ft->encoding.encoding = SOX_ENCODING_CL_ADPCM;
+      v->size = 4;
+      break;
+    case VOC_FMT_CRLADPCM3:    /*     2    Creative 8-bit to 3-bit ADPCM */
+      ft->encoding.encoding = SOX_ENCODING_CL_ADPCM;
+      v->size = 3;
+      break;
+    case VOC_FMT_CRLADPCM2:    /*     3    Creative 8-bit to 2-bit ADPCM */
+      ft->encoding.encoding = SOX_ENCODING_CL_ADPCM;
+      v->size = 2;
+      break;
+    case VOC_FMT_LIN16:        /*     4    16-bit signed PCM */
+      ft->encoding.encoding = SOX_ENCODING_SIGN2;
+      v->size = 16;
+      break;
+    case VOC_FMT_ALAW: /*     6    CCITT a-Law 8-bit PCM */
+      ft->encoding.encoding = SOX_ENCODING_ALAW;
+      v->size = 8;
+      break;
+    case VOC_FMT_MU255:        /*     7    CCITT u-Law 8-bit PCM */
+      ft->encoding.encoding = SOX_ENCODING_ULAW;
+      v->size = 8;
+      break;
+    case VOC_FMT_CRLADPCM4A:   /*0x200    Creative 16-bit to 4-bit ADPCM */
+      ft->encoding.encoding = SOX_ENCODING_CL_ADPCM16;
+      v->size = 4;
+      break;
+    default:
+      sox_fail("Unknown VOC format %d", v->format);
+      rtn = SOX_EOF;
+      break;
+  }
+  ft->encoding.bits_per_sample = v->size;
 
-        /* setup number of channels */
-        if (ft->signal.channels == 0)
-                ft->signal.channels = v->channels;
+  /* setup number of channels */
+  if (ft->signal.channels == 0)
+    ft->signal.channels = v->channels;
 
-        return(SOX_SUCCESS);
+  return (SOX_SUCCESS);
 }
 
 /*-----------------------------------------------------------------
- * sox_vocread() -- read data from a VOC file
+ * read() -- read data from a VOC file
  * ANN:  Major changes here to support multi-part files and files
  *       that do not have audio in block 9's.
  *-----------------------------------------------------------------*/
-static sox_size_t sox_vocread(sox_format_t * ft, sox_sample_t *buf, sox_size_t len)
+static sox_size_t read_samples(sox_format_t * ft, sox_sample_t * buf,
+                               sox_size_t len)
 {
-        vs_t v = (vs_t) ft->priv;
-        sox_size_t done = 0;
-        int rc = 0;
-        int16_t sw;
-        unsigned char  uc;
+  vs_t v = (vs_t) ft->priv;
+  sox_size_t done = 0;
+  int rc = 0;
+  int16_t sw;
+  unsigned char uc;
 
-        /* handle getting another cont. buffer */
-        if (v->rest == 0)
-        {
-                rc = getblock(ft);
-                if (rc)
-                    return 0;
-        }
+  if (v->block_remaining == 0) {        /* handle getting another cont. buffer */
+    rc = getblock(ft);
+    if (rc)
+      return 0;
+  }
+
+  if (v->block_remaining == 0)  /* if no more data, return 0, i.e., done */
+    return 0;
 
-        /* if no more data, return 0, i.e., done */
-        if (v->rest == 0)
-                return 0;
+  if (v->silent) {
+    for (; v->block_remaining && (done < len); v->block_remaining--, done++)
+      *buf++ = 0;       /* Fill in silence */
+  } else {      /* not silence; read len samples of audio from the file */
+    sox_size_t per = max(1, 9 / v->size);
 
-        /* if silence, fill it in with 0's */
-        if (v->silent) {
-                /* Fill in silence */
-                for(;v->rest && (done < len); v->rest--, done++)
-                        *buf++ = 0x80000000;
+    for (; (done + per <= len); done += per) {
+      if (v->block_remaining == 0) {    /* IF no more in this block, get another */
+        while (v->block_remaining == 0) {       /* until have either EOF or a block with data */
+          rc = getblock(ft);
+          if (rc)
+            break;
         }
-        /* else, not silence, read the block */
-        else {
-            /* read len samples of audio from the file */
+        if (rc) /* IF EOF, break out, no more data, next will return 0 */
+          break;
+      }
 
-            /* for(;v->rest && (done < len); v->rest--, done++) { */
-            for(; (done < len); done++) {
+      /* Read the data in the file */
+      if (v->size <= 4) {
+        if (!v->adpcm.setup.sign) {
+          if (sox_readb(ft, &uc) == SOX_EOF) {
+            sox_warn("VOC input: short file");
+            v->block_remaining = 0;
+            return done;
+          }
+          *buf = SOX_UNSIGNED_8BIT_TO_SAMPLE(uc,);
+          adpcm_init(&v->adpcm, 6 - v->size, SOX_SAMPLE_TO_SIGNED_16BIT(*buf, ft->clips));
+          ++buf;
+          --v->block_remaining;
+          ++done;
+        }
+        if (sox_readb(ft, &uc) == SOX_EOF) {
+          sox_warn("VOC input: short file");
+          v->block_remaining = 0;
+          return done;
+        }
+        switch (v->size) {
+          case 2:
+            if (v->format == VOC_FMT_CRLADPCM2) {
+              int u = uc;
 
-                /* IF no more in this block, get another */
-                if (v->rest == 0) {
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u >> 6, &v->adpcm),);
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u >> 4, &v->adpcm),);
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u >> 2, &v->adpcm),);
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u     , &v->adpcm),);
+            }
+            break;
+          case 3:
+            if (v->format == VOC_FMT_CRLADPCM3) {
+              int u = uc;
 
-                    /* DO until we have either EOF or a block with data */
-                    while (v->rest == 0) {
-                        rc = getblock(ft);
-                        if (rc)
-                            break;
-                    }
-                    /* ENDDO ... */
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u >> 5, &v->adpcm),);
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u >> 2, &v->adpcm),);
+              *buf++ =                              /* A bit from nowhere! */
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u << 1, &v->adpcm),);
+            }
+            break;
+          case 4:
+            if (v->format == VOC_FMT_CRLADPCM4) {
+              int u = uc;
 
-                    /* IF EOF, break out, no more data, next will return 0 */
-                    if (rc)
-                        break;
-                }
-                /* ENDIF no more data in block */
-
-                /* Read the data in the file */
-                switch(v->size) {
-                case 8:
-                    if (sox_readb(ft, &uc) == SOX_EOF) {
-                        sox_warn("VOC input: short file");
-                        v->rest = 0;
-                        return done;
-                    }
-                    /* IF uLaw,alaw, expand to linear, else convert??? */
-                    /* ANN:  added uLaw and aLaw support */
-                    if (v->format == VOC_FMT_MU255) {
-                        *buf++ =  SOX_ULAW_BYTE_TO_SAMPLE(uc);
-                    } else if (v->format == VOC_FMT_ALAW) {
-                        *buf++ =  SOX_ALAW_BYTE_TO_SAMPLE(uc);
-                    } else {
-                        *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(uc,);
-                    }
-                    break;
-                case 16:
-                    sox_readw(ft, (unsigned short *)&sw);
-                    if (sox_eof(ft))
-                        {
-                            sox_warn("VOC input: short file");
-                            v->rest = 0;
-                            return done;
-                        }
-                    *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(sw,);
-                    v->rest--; /* Processed 2 bytes so update */
-                    break;
-                }
-                /* decrement count of processed bytes */
-                v->rest--; /* Processed 2 bytes so update */
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u >> 4, &v->adpcm),);
+              *buf++ =
+                  SOX_SIGNED_16BIT_TO_SAMPLE(adpcm_decode (u     , &v->adpcm),);
+            }
+            break;
+        }
+      } else
+        switch (v->size) {
+          case 8:
+            if (sox_readb(ft, &uc) == SOX_EOF) {
+              sox_warn("VOC input: short file");
+              v->block_remaining = 0;
+              return done;
             }
+            if (v->format == VOC_FMT_MU255) {
+              *buf++ = SOX_ULAW_BYTE_TO_SAMPLE(uc);
+            } else if (v->format == VOC_FMT_ALAW) {
+              *buf++ = SOX_ALAW_BYTE_TO_SAMPLE(uc);
+            } else {
+              *buf++ = SOX_UNSIGNED_8BIT_TO_SAMPLE(uc,);
+            }
+            break;
+          case 16:
+            sox_readw(ft, (unsigned short *) &sw);
+            if (sox_eof(ft)) {
+              sox_warn("VOC input: short file");
+              v->block_remaining = 0;
+              return done;
+            }
+            *buf++ = SOX_SIGNED_16BIT_TO_SAMPLE(sw,);
+            v->block_remaining--;       /* Processed 2 bytes so update */
+            break;
         }
-        v->total_size+=done;
-        return done;
+      /* decrement count of processed bytes */
+      v->block_remaining--;
+    }
+  }
+  v->total_size += done;
+  return done;
 }
 
+
 /* When saving samples in VOC format the following outline is followed:
  * If an 8-bit mono sample then use a VOC_DATA header.
  * If an 8-bit stereo sample then use a VOC_EXTENDED header followed
@@ -409,58 +453,55 @@
  * which will work with the oldest software (eg. an 8-bit mono sample
  * will be able to be played with a really old SB VOC player.)
  */
-static int sox_vocstartwrite(sox_format_t * ft)
+static int startwrite(sox_format_t * ft)
 {
-        vs_t v = (vs_t) ft->priv;
+  vs_t v = (vs_t) ft->priv;
 
-        if (! ft->seekable)
-        {
-                sox_fail_errno(ft,SOX_EOF,
-                              "Output .voc file must be a file, not a pipe");
-                return(SOX_EOF);
-        }
+  if (!ft->seekable) {
+    sox_fail_errno(ft, SOX_EOF,
+                   "Output .voc file must be a file, not a pipe");
+    return (SOX_EOF);
+  }
 
-        v->samples = 0;
+  v->samples = 0;
 
-        /* File format name and a ^Z (aborts printing under DOS) */
-        sox_writes(ft, "Creative Voice File\032");
-        sox_writew(ft, 26);                      /* size of header */
-        sox_writew(ft, 0x10a);              /* major/minor version number */
-        sox_writew(ft, 0x1129);          /* checksum of version number */
+  /* File format name and a ^Z (aborts printing under DOS) */
+  sox_writes(ft, "Creative Voice File\032");
+  sox_writew(ft, 26);   /* size of header */
+  sox_writew(ft, 0x10a);        /* major/minor version number */
+  sox_writew(ft, 0x1129);       /* checksum of version number */
 
-        if (ft->signal.channels == 0)
-                ft->signal.channels = 1;
-
-        return(SOX_SUCCESS);
+  return (SOX_SUCCESS);
 }
 
 /*-----------------------------------------------------------------
- * sox_vocwrite() -- write a VOC file
+ * write() -- write a VOC file
  *-----------------------------------------------------------------*/
-static sox_size_t sox_vocwrite(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 len)
 {
-        vs_t v = (vs_t) ft->priv;
-        unsigned char uc;
-        int16_t sw;
-        sox_size_t done = 0;
+  vs_t v = (vs_t) ft->priv;
+  unsigned char uc;
+  int16_t sw;
+  sox_size_t done = 0;
 
-        if (v->samples == 0) {
-          /* No silence packing yet. */
-          v->silent = 0;
-          blockstart(ft);
-        }
-        v->samples += len;
-        while(done < len) {
-          if (ft->encoding.bits_per_sample == 8) {
-            uc = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips);
-            sox_writeb(ft, uc);
-          } else {
-            sw = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
-            sox_writesw(ft,sw);
-          }
-          done++;
-        }
-        return done;
+  if (v->samples == 0) {
+    /* No silence packing yet. */
+    v->silent = 0;
+    blockstart(ft);
+  }
+  v->samples += len;
+  while (done < len) {
+    if (ft->encoding.bits_per_sample == 8) {
+      uc = SOX_SAMPLE_TO_UNSIGNED_8BIT(*buf++, ft->clips);
+      sox_writeb(ft, uc);
+    } else {
+      sw = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
+      sox_writesw(ft, sw);
+    }
+    done++;
+  }
+  return done;
 }
 
 /*-----------------------------------------------------------------
@@ -469,37 +510,37 @@
  *-----------------------------------------------------------------*/
 static void blockstop(sox_format_t * ft)
 {
-        vs_t v = (vs_t) ft->priv;
-        sox_sample_t datum;
+  vs_t v = (vs_t) ft->priv;
+  sox_sample_t datum;
 
-        sox_writeb(ft, 0);                     /* End of file block code */
-        sox_seeki(ft, (sox_ssize_t)v->blockseek, 0); /* seek back to block length */
-        sox_seeki(ft, 1, 1);                    /* seek forward one */
-        if (v->silent) {
-                sox_writesw(ft, v->samples);
-        } else {
-          if (ft->encoding.bits_per_sample == 8) {
-            if (ft->signal.channels > 1) {
-              sox_seeki(ft, 8, 1); /* forward 7 + 1 for new block header */
-            }
-          }
-                v->samples += 2;                /* adjustment: SBDK pp. 3-5 */
-                datum = (v->samples * (ft->encoding.bits_per_sample >> 3)) & 0xff;
-                sox_writesb(ft, datum);       /* low byte of length */
-                datum = ((v->samples * (ft->encoding.bits_per_sample >> 3)) >> 8) & 0xff;
-                sox_writesb(ft, datum);  /* middle byte of length */
-                datum = ((v->samples  * (ft->encoding.bits_per_sample >> 3))>> 16) & 0xff;
-                sox_writesb(ft, datum); /* high byte of length */
-        }
+  sox_writeb(ft, 0);    /* End of file block code */
+  sox_seeki(ft, (sox_ssize_t) v->blockseek, 0); /* seek back to block length */
+  sox_seeki(ft, 1, 1);  /* seek forward one */
+  if (v->silent) {
+    sox_writesw(ft, v->samples);
+  } else {
+    if (ft->encoding.bits_per_sample == 8) {
+      if (ft->signal.channels > 1) {
+        sox_seeki(ft, 8, 1);    /* forward 7 + 1 for new block header */
+      }
+    }
+    v->samples += 2;    /* adjustment: SBDK pp. 3-5 */
+    datum = (v->samples * (ft->encoding.bits_per_sample >> 3)) & 0xff;
+    sox_writesb(ft, datum);     /* low byte of length */
+    datum = ((v->samples * (ft->encoding.bits_per_sample >> 3)) >> 8) & 0xff;
+    sox_writesb(ft, datum);     /* middle byte of length */
+    datum = ((v->samples * (ft->encoding.bits_per_sample >> 3)) >> 16) & 0xff;
+    sox_writesb(ft, datum);     /* high byte of length */
+  }
 }
 
 /*-----------------------------------------------------------------
- * sox_vocstopwrite() -- stop writing a VOC file
+ * stopwrite() -- stop writing a VOC file
  *-----------------------------------------------------------------*/
-static int sox_vocstopwrite(sox_format_t * ft)
+static int stopwrite(sox_format_t * ft)
 {
-        blockstop(ft);
-        return(SOX_SUCCESS);
+  blockstop(ft);
+  return (SOX_SUCCESS);
 }
 
 /*-----------------------------------------------------------------
@@ -512,226 +553,176 @@
  *-----------------------------------------------------------------*/
 static int getblock(sox_format_t * ft)
 {
-        vs_t v = (vs_t) ft->priv;
-        unsigned char uc, block;
-        uint32_t sblen;
-        uint16_t new_rate_16;
-        uint32_t new_rate_32;
-        uint32_t i;
-        uint32_t trash;
+  vs_t v = (vs_t) ft->priv;
+  unsigned char uc, block;
+  uint24_t sblen;
+  uint16_t new_rate_16;
+  uint32_t new_rate_32;
+  uint32_t i;
+  uint32_t trash;
 
-        v->silent = 0;
-        /* DO while we have no audio to read */
-        while (v->rest == 0) {
-                /* IF EOF, return EOF
-                 * ANN:  was returning SUCCESS */
-                if (sox_eof(ft))
-                        return SOX_EOF;
+  v->silent = 0;
+  /* DO while we have no audio to read */
+  while (v->block_remaining == 0) {
+    if (sox_eof(ft))
+      return SOX_EOF;
 
-                if (sox_readb(ft, &block) == SOX_EOF)
-                        return SOX_EOF;
+    if (sox_readb(ft, &block) == SOX_EOF)
+      return SOX_EOF;
 
-                /* IF TERM block (end of file), return EOF */
-                if (block == VOC_TERM)
-                        return SOX_EOF;
+    if (block == VOC_TERM)
+      return SOX_EOF;
 
-                /* IF EOF after reading block type, return EOF
-                 * ANN:  was returning SUCCESS */
-                if (sox_eof(ft))
-                        return SOX_EOF;
-                /*
-                 * Size is an 24-bit value.  Currently there is no util
-                 * func to read this so do it this cross-platform way
-                 *
-                 */
-                sox_readb(ft, &uc);
-                sblen = uc;
-                sox_readb(ft, &uc);
-                sblen |= ((uint32_t) uc) << 8;
-                sox_readb(ft, &uc);
-                sblen |= ((uint32_t) uc) << 16;
+    if (sox_eof(ft))
+      return SOX_EOF;
 
-                /* Based on VOC block type, process the block */
-                /* audio may be in one or multiple blocks */
-                switch(block) {
-                case VOC_DATA:
-                        sox_readb(ft, &uc);
-                        /* When DATA block preceeded by an EXTENDED     */
-                        /* block, the DATA blocks rate value is invalid */
-                        if (!v->extended) {
-                          if (uc == 0)
-                          {
-                            sox_fail_errno(ft, SOX_EFMT, "Sample rate is zero?");
-                            return(SOX_EOF);
-                          }
-                          if ((v->rate != -1) && (uc != v->rate))
-                          {
-                            sox_fail_errno(ft,SOX_EFMT,
-                              "sample rate codes differ: %ld != %d", v->rate, uc);
-                            return(SOX_EOF);
-                          }
-                          v->rate = uc;
-                          ft->signal.rate = 1000000.0/(256 - v->rate);
-                          v->channels = 1;
-                        }
-                        sox_readb(ft, &uc);
-                        if (uc != 0)
-                        {
-                          sox_fail_errno(ft,SOX_EFMT,
-                            "only interpret 8-bit data!");
-                          return(SOX_EOF);
-                        }
-                        v->extended = 0;
-                        v->rest = sblen - 2;
-                        v->size = 8;
-                        return (SOX_SUCCESS);
-                case VOC_DATA_16:
-                        sox_readdw(ft, &new_rate_32);
-                        if (new_rate_32 == 0)
-                        {
-                            sox_fail_errno(ft,SOX_EFMT,
-                              "Sample rate is zero?");
-                            return(SOX_EOF);
-                        }
-                        if ((v->rate != -1) && ((long)new_rate_32 != v->rate))
-                        {
-                            sox_fail_errno(ft,SOX_EFMT,
-                              "sample rate codes differ: %ld != %d",
-                                v->rate, new_rate_32);
-                            return(SOX_EOF);
-                        }
-                        v->rate = new_rate_32;
-                        ft->signal.rate = new_rate_32;
-                        sox_readb(ft, &uc);
-                        switch (uc)
-                        {
-                            case 8:     v->size = 8; break;
-                            case 16:    v->size = 16; break;
-                            default:
-                                sox_fail_errno(ft,SOX_EFMT,
-                                              "Don't understand size %d", uc);
-                                return(SOX_EOF);
-                        }
-                        sox_readb(ft, &(v->channels));
-                        sox_readw(ft, &(v->format));  /* ANN: added format */
-                        sox_readb(ft, (unsigned char *)&trash); /* notused */
-                        sox_readb(ft, (unsigned char *)&trash); /* notused */
-                        sox_readb(ft, (unsigned char *)&trash); /* notused */
-                        sox_readb(ft, (unsigned char *)&trash); /* notused */
-                        v->rest = sblen - 12;
-                        return (SOX_SUCCESS);
-                case VOC_CONT:
-                        v->rest = sblen;
-                        return (SOX_SUCCESS);
-                case VOC_SILENCE:
-                        {
-                        unsigned short period;
+    sox_read3(ft, &sblen);
+
+    /* Based on VOC block type, process the block */
+    /* audio may be in one or multiple blocks */
+    switch (block) {
+      case VOC_DATA:
+        sox_readb(ft, &uc);
+        /* When DATA block preceeded by an EXTENDED     */
+        /* block, the DATA blocks rate value is invalid */
+        if (!v->extended) {
+          if (uc == 0) {
+            sox_fail_errno(ft, SOX_EFMT, "Sample rate is zero?");
+            return (SOX_EOF);
+          }
+          if ((v->rate != -1) && (uc != v->rate)) {
+            sox_fail_errno(ft, SOX_EFMT,
+                           "sample rate codes differ: %ld != %d", v->rate,
+                           uc);
+            return (SOX_EOF);
+          }
+          v->rate = uc;
+          ft->signal.rate = 1000000.0 / (256 - v->rate);
+          v->channels = 1;
+        }
+        sox_readb(ft, &uc);
+        v->format = uc;
+        v->extended = 0;
+        v->block_remaining = sblen - 2;
+        return (SOX_SUCCESS);
+      case VOC_DATA_16:
+        sox_readdw(ft, &new_rate_32);
+        if (new_rate_32 == 0) {
+          sox_fail_errno(ft, SOX_EFMT, "Sample rate is zero?");
+          return (SOX_EOF);
+        }
+        if ((v->rate != -1) && ((long) new_rate_32 != v->rate)) {
+          sox_fail_errno(ft, SOX_EFMT, "sample rate codes differ: %ld != %d",
+                         v->rate, new_rate_32);
+          return (SOX_EOF);
+        }
+        v->rate = new_rate_32;
+        ft->signal.rate = new_rate_32;
+        sox_readb(ft, &uc);
+        v->size = uc;
+        sox_readb(ft, &(v->channels));
+        sox_readw(ft, &(v->format));    /* ANN: added format */
+        sox_readb(ft, (unsigned char *) &trash);        /* notused */
+        sox_readb(ft, (unsigned char *) &trash);        /* notused */
+        sox_readb(ft, (unsigned char *) &trash);        /* notused */
+        sox_readb(ft, (unsigned char *) &trash);        /* notused */
+        v->block_remaining = sblen - 12;
+        return (SOX_SUCCESS);
+      case VOC_CONT:
+        v->block_remaining = sblen;
+        return (SOX_SUCCESS);
+      case VOC_SILENCE:
+        {
+          unsigned short period;
 
-                        sox_readw(ft, &period);
-                        sox_readb(ft, &uc);
-                        if (uc == 0)
-                        {
-                          sox_fail_errno(ft,SOX_EFMT, "Silence sample rate is zero");
-                          return(SOX_EOF);
-                        }
-                        /*
-                         * Some silence-packed files have gratuitously
-                         * different sample rate codes in silence.
-                         * Adjust period.
-                         */
-                        if ((v->rate != -1) && (uc != v->rate))
-                                period = (period * (256 - uc))/(256 - v->rate);
-                        else
-                                v->rate = uc;
-                        v->rest = period;
-                        v->silent = 1;
-                        return (SOX_SUCCESS);
-                        }
-                case VOC_MARKER:
-                        sox_readb(ft, &uc);
-                        sox_readb(ft, &uc);
-                        /* Falling! Falling! */
-                case VOC_TEXT:
-                        { 
-                          /* TODO: Could add to comment in SF? */
-
-                          /* Note, if this is sent to stderr, studio */
-                          /* will not be able to read the VOC file */
-
-                          uint32_t i = sblen;
-                          char c/*, line_buf[80];
-                          int len = 0*/;
+          sox_readw(ft, &period);
+          sox_readb(ft, &uc);
+          if (uc == 0) {
+            sox_fail_errno(ft, SOX_EFMT, "Silence sample rate is zero");
+            return (SOX_EOF);
+          }
+          /*
+           * Some silence-packed files have gratuitously
+           * different sample rate codes in silence.
+           * Adjust period.
+           */
+          if ((v->rate != -1) && (uc != v->rate))
+            period = (period * (256. - uc)) / (256 - v->rate) + .5;
+          else
+            v->rate = uc;
+          v->block_remaining = period;
+          v->silent = 1;
+          return (SOX_SUCCESS);
+        }
+      case VOC_MARKER:
+        sox_readb(ft, &uc);
+        sox_readb(ft, &uc);
+        /* Falling! Falling! */
+      case VOC_TEXT:
+        {
+          uint32_t i = sblen;
+          char c                /*, line_buf[80];
+                                 * int len = 0 */ ;
 
-                          while (i--)
-                          {
-                            sox_readb(ft, (unsigned char *)&c);
-                            /* FIXME: this needs to be tested but I couldn't
-                             * find a voc file with a VOC_TEXT chunk :(
-                            if (c != '\0' && c != '\r')
-                              line_buf[len++] = c;
-                            if (len && (c == '\0' || c == '\r' ||
-                                i == 0 || len == sizeof(line_buf) - 1))
-                            {
-                              sox_report("%s", line_buf);
-                              line_buf[len] = '\0';
-                              len = 0;
-                            }
-                            */
-                          }
-                        }
-                        continue;       /* get next block */
-                case VOC_LOOP:
-                case VOC_LOOPEND:
-                        sox_debug("skipping repeat loop");
-                        for(i = 0; i < sblen; i++)
-                            sox_readb(ft, (unsigned char *)&trash);
-                        break;
-                case VOC_EXTENDED:
-                        /* An Extended block is followed by a data block */
-                        /* Set this byte so we know to use the rate      */
-                        /* value from the extended block and not the     */
-                        /* data block.                                   */
-                        v->extended = 1;
-                        sox_readw(ft, &new_rate_16);
-                        if (new_rate_16 == 0)
-                        {
-                           sox_fail_errno(ft,SOX_EFMT, "Sample rate is zero?");
-                           return(SOX_EOF);
-                        }
-                        if ((v->rate != -1) && (new_rate_16 != v->rate))
-                        {
-                           sox_fail_errno(ft,SOX_EFMT,
-                             "sample rate codes differ: %ld != %d",
-                                        v->rate, new_rate_16);
-                           return(SOX_EOF);
-                        }
-                        v->rate = new_rate_16;
-                        sox_readb(ft, &uc);
-                        if (uc != 0)
-                        {
-                                sox_fail_errno(ft,SOX_EFMT,
-                                  "only interpret 8-bit data!");
-                                return(SOX_EOF);
-                        }
-                        sox_readb(ft, &uc);
-                        if (uc)
-                                ft->signal.channels = 2;  /* Stereo */
-                        /* Needed number of channels before finishing
-                           compute for rate */
-                        ft->signal.rate = (256000000/(65536 - v->rate))/
-                            ft->signal.channels;
-                        /* An extended block must be followed by a data */
-                        /* block to be valid so loop back to top so it  */
-                        /* can be grabed.                               */
-                        continue;
-                default:
-                        sox_debug("skipping unknown block code %d", block);
-                        for(i = 0; i < sblen; i++)
-                            sox_readb(ft, (unsigned char *)&trash);
-                }
-        }
-        return SOX_SUCCESS;
-}
+          sox_warn("VOC TEXT");
+          while (i--) {
+            sox_readb(ft, (unsigned char *) &c);
+            /* FIXME: this needs to be tested but I couldn't
+             * find a voc file with a VOC_TEXT chunk :(
+             if (c != '\0' && c != '\r')
+             line_buf[len++] = c;
+             if (len && (c == '\0' || c == '\r' ||
+             i == 0 || len == sizeof(line_buf) - 1))
+             {
+             sox_report("%s", line_buf);
+             line_buf[len] = '\0';
+             len = 0;
+             }
+             */
+          }
+        }
+        continue;       /* get next block */
+      case VOC_LOOP:
+      case VOC_LOOPEND:
+        sox_debug("skipping repeat loop");
+        for (i = 0; i < sblen; i++)
+          sox_readb(ft, (unsigned char *) &trash);
+        break;
+      case VOC_EXTENDED:
+        /* An Extended block is followed by a data block */
+        /* Set this byte so we know to use the rate      */
+        /* value from the extended block and not the     */
+        /* data block.                                   */
+        v->extended = 1;
+        sox_readw(ft, &new_rate_16);
+        if (new_rate_16 == 0) {
+          sox_fail_errno(ft, SOX_EFMT, "Sample rate is zero?");
+          return (SOX_EOF);
+        }
+        if ((v->rate != -1) && (new_rate_16 != v->rate)) {
+          sox_fail_errno(ft, SOX_EFMT, "sample rate codes differ: %ld != %d",
+                         v->rate, new_rate_16);
+          return (SOX_EOF);
+        }
+        v->rate = new_rate_16;
+        sox_readb(ft, &uc); /* bits_per_sample */
+        sox_readb(ft, &uc);
+        ft->signal.channels = uc? 2 : 1;      /* Stereo */
+        /* Needed number of channels before finishing
+         * compute for rate */
+        ft->signal.rate = (256e6 / (65536 - v->rate)) / ft->signal.channels;
+        /* An extended block must be followed by a data */
+        /* block to be valid so loop back to top so it  */
+        /* can be grabed.                               */
+        continue;
+      default:
+        sox_debug("skipping unknown block code %d", block);
+        for (i = 0; i < sblen; i++)
+          sox_readb(ft, (unsigned char *) &trash);
+    }
+  }
+  return SOX_SUCCESS;
+}
 
 /*-----------------------------------------------------------------
  * vlockstart() -- start an output block
@@ -738,68 +729,69 @@
  *-----------------------------------------------------------------*/
 static void blockstart(sox_format_t * ft)
 {
-        vs_t v = (vs_t) ft->priv;
+  vs_t v = (vs_t) ft->priv;
 
-        v->blockseek = sox_tell(ft);
-        if (v->silent) {
-                sox_writeb(ft, VOC_SILENCE);     /* Silence block code */
-                sox_writeb(ft, 0);               /* Period length */
-                sox_writeb(ft, 0);               /* Period length */
-                sox_writesb(ft, v->rate);         /* Rate code */
-        } else {
-          if (ft->encoding.bits_per_sample == 8) {
-            /* 8-bit sample section.  By always setting the correct     */
-            /* rate value in the DATA block (even when its preceeded    */
-            /* by an EXTENDED block) old software can still play stereo */
-            /* files in mono by just skipping over the EXTENDED block.  */
-            /* Prehaps the rate should be doubled though to make up for */
-            /* double amount of samples for a given time????            */
-            if (ft->signal.channels > 1) {
-              sox_writeb(ft, VOC_EXTENDED);      /* Voice Extended block code */
-              sox_writeb(ft, 4);                /* block length = 4 */
-              sox_writeb(ft, 0);                /* block length = 4 */
-              sox_writeb(ft, 0);                /* block length = 4 */
-                  v->rate = 65536 - (256000000.0/(2*(float)ft->signal.rate));
-              sox_writesw(ft,v->rate);    /* Rate code */
-              sox_writeb(ft, 0);         /* File is not packed */
-              sox_writeb(ft, 1);         /* samples are in stereo */
-            }
-            sox_writeb(ft, VOC_DATA);    /* Voice Data block code */
-            sox_writeb(ft, 0);           /* block length (for now) */
-            sox_writeb(ft, 0);           /* block length (for now) */
-            sox_writeb(ft, 0);           /* block length (for now) */
-            v->rate = 256 - (1000000.0/(float)ft->signal.rate);
-            sox_writesb(ft, (int) v->rate);/* Rate code */
-            sox_writeb(ft, 0);           /* 8-bit raw data */
-        } else {
-            sox_writeb(ft, VOC_DATA_16); /* Voice Data block code */
-            sox_writeb(ft, 0);           /* block length (for now) */
-            sox_writeb(ft, 0);           /* block length (for now) */
-            sox_writeb(ft, 0);           /* block length (for now) */
-            v->rate = ft->signal.rate;
-            sox_writedw(ft, (unsigned)v->rate);    /* Rate code */
-            sox_writeb(ft, 16);          /* Sample Size */
-            sox_writeb(ft, ft->signal.channels);   /* Sample Size */
-            sox_writew(ft, 0x0004);      /* Encoding */
-            sox_writeb(ft, 0);           /* Unused */
-            sox_writeb(ft, 0);           /* Unused */
-            sox_writeb(ft, 0);           /* Unused */
-            sox_writeb(ft, 0);           /* Unused */
-          }
-        }
+  v->blockseek = sox_tell(ft);
+  if (v->silent) {
+    sox_writeb(ft, VOC_SILENCE);        /* Silence block code */
+    sox_writeb(ft, 0);  /* Period length */
+    sox_writeb(ft, 0);  /* Period length */
+    sox_writesb(ft, v->rate);   /* Rate code */
+  } else {
+    if (ft->encoding.bits_per_sample == 8) {
+      /* 8-bit sample section.  By always setting the correct     */
+      /* rate value in the DATA block (even when its preceeded    */
+      /* by an EXTENDED block) old software can still play stereo */
+      /* files in mono by just skipping over the EXTENDED block.  */
+      /* Prehaps the rate should be doubled though to make up for */
+      /* double amount of samples for a given time????            */
+      if (ft->signal.channels > 1) {
+        sox_writeb(ft, VOC_EXTENDED);   /* Voice Extended block code */
+        sox_writeb(ft, 4);      /* block length = 4 */
+        sox_writeb(ft, 0);      /* block length = 4 */
+        sox_writeb(ft, 0);      /* block length = 4 */
+        v->rate = 65536 - (256000000.0 / (2 * ft->signal.rate)) + .5;
+        sox_writesw(ft, v->rate);       /* Rate code */
+        sox_writeb(ft, 0);      /* File is not packed */
+        sox_writeb(ft, 1);      /* samples are in stereo */
+      }
+      sox_writeb(ft, VOC_DATA); /* Voice Data block code */
+      sox_writeb(ft, 0);        /* block length (for now) */
+      sox_writeb(ft, 0);        /* block length (for now) */
+      sox_writeb(ft, 0);        /* block length (for now) */
+      v->rate = 256 - (1000000.0 / ft->signal.rate) + .5;
+      sox_writesb(ft, v->rate); /* Rate code */
+      sox_writeb(ft, 0);        /* 8-bit raw data */
+    } else {
+      sox_writeb(ft, VOC_DATA_16);      /* Voice Data block code */
+      sox_writeb(ft, 0);        /* block length (for now) */
+      sox_writeb(ft, 0);        /* block length (for now) */
+      sox_writeb(ft, 0);        /* block length (for now) */
+      v->rate = ft->signal.rate + .5;
+      sox_writedw(ft, (unsigned) v->rate);      /* Rate code */
+      sox_writeb(ft, 16);       /* Sample Size */
+      sox_writeb(ft, ft->signal.channels);      /* Sample Size */
+      sox_writew(ft, 0x0004);   /* Encoding */
+      sox_writeb(ft, 0);        /* Unused */
+      sox_writeb(ft, 0);        /* Unused */
+      sox_writeb(ft, 0);        /* Unused */
+      sox_writeb(ft, 0);        /* Unused */
+    }
+  }
 }
 
 SOX_FORMAT_HANDLER(voc)
 {
-  static char const * const names[] = {"voc", NULL};
+  static char const *const names[] = { "voc", NULL };
   static unsigned const write_encodings[] = {
     SOX_ENCODING_SIGN2, 16, 0,
     SOX_ENCODING_UNSIGNED, 8, 0,
-    0};
+    0
+  };
   static sox_format_handler_t const handler = {
-    names, SOX_FILE_LIT_END|SOX_FILE_MONO|SOX_FILE_STEREO,
-    sox_vocstartread, sox_vocread, NULL,
-    sox_vocstartwrite, sox_vocwrite, sox_vocstopwrite,
+    names, SOX_FILE_LIT_END | SOX_FILE_MONO | SOX_FILE_STEREO,
+    startread, read_samples, NULL,
+    startwrite, write_samples, stopwrite,
     NULL, write_encodings, NULL
   };
   return &handler;