shithub: sox

Download patch

ref: b0aa26c836d4397fce0a86d100e1b76116e5ced0
parent: 9343c9427158a36841cbece5baff21bd26505c7d
author: robs <robs>
date: Tue Jan 9 17:05:18 EST 2007

Fix [ 1160154 ] VOX to WAV conversion problem.

--- a/ChangeLog
+++ b/ChangeLog
@@ -87,6 +87,7 @@
     bit reversal) [FR# 1621702].  (robs)
   o Add seek support for GSM data in WAV files. Rafal Maszkowski
   o Fix [1627972] AIFF read bug when MARK chunk present.  (Richard Fuller)
+  o Fix [1160154] VOX to WAV conversion problem.  (robs)
 
 sox-12.18.2
 -----------
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,12 +13,12 @@
 lib_LTLIBRARIES = libst.la
 include_HEADERS = st.h stconfig.h ststdint.h
 
-formats = 8svx.c adpcm.c adpcm.h aiff.c au.c auto.c avr.c cdr.c \
-	  cvsd.c cvsdfilt.h dat.c flac.c g711.c g711.h g721.c \
+formats = 8svx.c adpcm.c adpcm.h adpcms.c adpcms.h aiff.c au.c auto.c avr.c \
+	  cdr.c cvsd.c cvsdfilt.h dat.c flac.c g711.c g711.h g721.c \
 	  g723_16.c g723_24.c g723_40.c g72x.c g72x.h gsm.c hcom.c \
-	  ima_rw.c ima_rw.h maud.c mp3.c nulfile.c prc.c raw.c \
-	  sf.c sfircam.h smp.c sndrtool.c sphere.c tx16w.c voc.c vorbis.c \
-	  vox.c wav.c wav.h wve.c xa.c
+	  ima_rw.c ima_rw.h maud.c mp3.c nulfile.c prc.c \
+	  raw.c sf.c sfircam.h smp.c sndrtool.c sphere.c tx16w.c voc.c \
+	  vorbis.c vox.c wav.c wav.h wve.c xa.c
 
 effects = avg.c band.c bandpass.c biquad.c biquad.h breject.c btrworth.c \
 	  btrworth.h chorus.c compand.c dcshift.c deemphas.c earwax.c \
--- /dev/null
+++ b/src/adpcms.c
@@ -1,0 +1,76 @@
+/*
+ * 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.
+ */
+
+/* ADPCM CODECs: IMA, OKI.   (c) 2007 robs@users.sourceforge.net */
+
+#include "adpcms.h"
+#include "st.h"
+
+#define range_limit(x,min,max)((x)<(min)?(min):(x)>(max)?(max):(x))
+
+static int const ima_steps[89] = { /* ~16-bit precision */
+  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,
+  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 };
+
+static int const oki_steps[49] = { /* ~12-bit precision */
+  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 };
+
+static int const step_changes[8] = {-1, -1, -1, -1, 2, 4, 6, 8};
+
+void adpcm_init(adpcm_t state, int type)
+{
+  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;
+}
+
+int adpcm_decode(int code, adpcm_t state)
+{
+  int s = ((code & 7) << 1) | 1;
+  s = ((state->steps[state->step_index] * s) >> 3) & state->mask;
+  if (code & 8)
+    s = -s;
+  s = state->last_output + s;
+  s = range_limit(s, -0x8000, 0x7fff);
+  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;
+}
+
+int adpcm_encode(int sample, adpcm_t state)
+{
+  int delta = sample - state->last_output;
+  int sign = 0;
+  int code;
+  if (delta < 0) {
+    sign = 0x08;
+    delta = -delta;
+  }
+  code = 4 * delta / state->steps[state->step_index];
+  code = sign | min(code, 7);
+  adpcm_decode(code, state); /* Update encoder state */
+  return code;
+}
--- /dev/null
+++ b/src/adpcms.h
@@ -1,0 +1,12 @@
+typedef struct adpcm_struct
+{
+  int last_output;
+  int step_index;
+  int max_step_index;
+  int const * steps;
+  int mask;
+} * adpcm_t;
+
+int adpcm_decode(int code, adpcm_t state);   /* 4-bit -> 16-bit */
+int adpcm_encode(int sample, adpcm_t state); /* 16-bit -> 4-bit */
+void adpcm_init(adpcm_t state, int type /* 0:IMA, 1:OKI */);
--- a/src/vox.c
+++ b/src/vox.c
@@ -1,3 +1,4 @@
+
 /************************************************************************
  *                                   SOX                                *
  *                                                                      *
@@ -5,25 +6,15 @@
  *                                                                      *
  * Project : SOX                                                        *
  * File    : vox.c                                                      *
- * Version : V12.17.4                                                   *
  *                                                                      *
  * Version History : V12.17.4 - Tony Seebregts                          *
  *                              5 May 2004                              *
- *                              1. Original                             *
  *                                                                      *
  * Description : SOX file format handler for Dialogic/Oki ADPCM VOX     *
  *               files.                                                 *
  *                                                                      *
- * Notes : 1. Based on the vox/devox code samples at:                   *
+ * Notes : Coded from SOX skeleton code supplied with SOX source.       *
  *                                                                      *
- *              http://www.cis.ksu.edu/~tim/vox                         *
- *                                                                      *
- *         2. Coded from SOX skeleton code supplied with SOX source.    *
- *                                                                      *
- *         3. Tested under:                                             *
- *            - Windows 2000 SP3/Visual C++ V6.0                        *
- *            - Windows 2000 SP3/Digital Mars V7.51                     *
- *                                                                      *
  ************************************************************************/
 
 /************************************************************************
@@ -39,36 +30,21 @@
  *                                                                      *
  ************************************************************************/
 
+#include "adpcms.h"
 #include "st_i.h"
 
+typedef struct voxstuff
+{
+  struct adpcm_struct encoder;
 
-typedef struct voxstuff { struct { short    last;                       /* ADPCM codec state */
-                                   short    index;
-                                 } state; 
+  struct {
+    uint8_t byte;               /* write store */
+    uint8_t flag;
+  } store;
+  st_fileinfo_t file;
+} *vox_t;
 
-                          struct { uint8_t  byte;                       /* write store */
-                                   uint8_t  flag;
-                                 } store;
-                          st_fileinfo_t file;
-                        } *vox_t;
 
-
-static short STEPSIZE[49] = { 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, 1060,1166,1282,1411,1552 
-                            };
-
-static short STEPADJUST[8] = { -1,-1,-1,-1,2,4,6,8 };
-  
-
-static uint8_t envox       (short,  vox_t);
-static short   devox       (uint8_t,vox_t);
-
-
 /******************************************************************************
  * Function   : st_voxstartread 
  * Description: Initialises the file parameters and ADPCM codec state.
@@ -82,34 +58,33 @@
  *                 rates but the codecs allows any user specified rate. 
  ******************************************************************************/
 
-static int  st_voxstartread (ft_t ft) 
-     { vox_t state = (vox_t) ft->priv;
+static int st_voxstartread(ft_t ft)
+{
+  vox_t state = (vox_t) ft->priv;
 
-       /* ... setup file info */
+  /* ... setup file info */
 
-       state->file.buf = (char *)xmalloc(ST_BUFSIZ);
-       state->file.size     = ST_BUFSIZ;
-       state->file.count    = 0;
-       state->file.pos      = 0;
-       state->file.eof      = 0;
+  state->file.buf = (char *) xmalloc(ST_BUFSIZ);
+  state->file.size = ST_BUFSIZ;
+  state->file.count = 0;
+  state->file.pos = 0;
+  state->file.eof = 0;
 
-       ft->signal.size     = ST_SIZE_WORD;
-       ft->signal.encoding = ST_ENCODING_OKI_ADPCM;
-       ft->signal.channels = 1;
-       if (ft->signal.rate == 0) {
-         st_warn("'%s': sample rate not specified; trying 8kHz", ft->filename);
-         ft->signal.rate = 8000;
-       }
+  ft->signal.size = ST_SIZE_WORD;
+  ft->signal.encoding = ST_ENCODING_OKI_ADPCM;
+  ft->signal.channels = 1;
+  if (ft->signal.rate == 0) {
+    st_warn("'%s': sample rate not specified; trying 8kHz", ft->filename);
+    ft->signal.rate = 8000;
+  }
 
-       /* ... initialise CODEC state */
+  adpcm_init(&state->encoder, 1);
 
-       state->state.last  = 0;
-       state->state.index = 0;
-       state->store.byte  = 0;
-       state->store.flag  = 0;
+  state->store.byte = 0;
+  state->store.flag = 0;
 
-           return (ST_SUCCESS);
-    }
+  return (ST_SUCCESS);
+}
 
 
 /******************************************************************************
@@ -125,47 +100,22 @@
  * Notes      : 
  ******************************************************************************/
 
-static st_size_t st_voxread (ft_t ft,st_sample_t *buffer,st_size_t length) 
-           { vox_t    state = (vox_t) ft->priv;
-             int      count = 0;
-             int      N;
-             uint8_t  byte;
-             short    word;
+static st_size_t st_voxread(ft_t ft, st_sample_t * buffer, st_size_t len)
+{
+  vox_t state = (vox_t) ft->priv;
+  st_size_t n;
+  uint8_t byte;
 
-             /* ... round length down to nearest even number */
+  for (n = 0; n < (len&~1) && st_readb(ft, &byte) == ST_SUCCESS; n += 2) {
+    short word = adpcm_decode(byte >> 4, &state->encoder);
+    *buffer++ = ST_SIGNED_WORD_TO_SAMPLE(word, ft->clippedCount);
 
-             N  = length/2;
-             N *=2;
+    word = adpcm_decode(byte, &state->encoder);
+    *buffer++ = ST_SIGNED_WORD_TO_SAMPLE(word, ft->clippedCount);
+  }
+  return n;
+}
 
-             /* ... loop until buffer full or EOF */
-
-             while (count < N) 
-               {     /* ... refill buffer */
-
-                     if (state->file.pos >= state->file.count)
-                        { state->file.count = st_readbuf (ft,state->file.buf,1,state->file.size);
-                          state->file.pos   = 0;
-
-                          if (state->file.count == 0)
-                             break;
-                        }
-
-                     /* ... decode two nybbles stored as a byte */
-
-                     byte      = state->file.buf[state->file.pos++];
-
-                     word      = devox ((uint8_t) ((byte >> 4) & 0x0F),state);
-                     *buffer++ = ST_SIGNED_WORD_TO_SAMPLE (word * 16,);
-
-                     word      = devox ((uint8_t) (byte & 0x0F),state);
-                     *buffer++ = ST_SIGNED_WORD_TO_SAMPLE (word * 16,);
-
-                     count += 2;
-                       }
-               
-             return count;
-           }
-
 /******************************************************************************
  * Function   : st_voxstopread 
  * Description: Frees the internal buffer allocated in st_voxstartread.
@@ -175,14 +125,16 @@
  * Notes      : 
  ******************************************************************************/
 
-static int  st_voxstopread (ft_t ft) 
-     { vox_t    state = (vox_t) ft->priv;
-       free (state->file.buf);
-     
-       return (ST_SUCCESS);
-     }
+static int st_voxstopread(ft_t ft)
+{
+  vox_t state = (vox_t) ft->priv;
 
+  free(state->file.buf);
 
+  return (ST_SUCCESS);
+}
+
+
 /******************************************************************************
  * Function   : st_voxstartwrite
  * Description: Initialises the file parameters and ADPCM codec state.
@@ -196,31 +148,30 @@
  *                 rates but the codecs allows any user specified rate. 
  ******************************************************************************/
 
-static int  st_voxstartwrite (ft_t ft) 
-     { vox_t state = (vox_t) ft->priv;
+static int st_voxstartwrite(ft_t ft)
+{
+  vox_t state = (vox_t) ft->priv;
 
 
-       /* ... setup file info */
+  /* ... setup file info */
 
-       state->file.buf = (char *)xmalloc(ST_BUFSIZ);
-       state->file.size     = ST_BUFSIZ;
-       state->file.count    = 0;
-       state->file.pos      = 0;
-       state->file.eof      = 0;
+  state->file.buf = (char *) xmalloc(ST_BUFSIZ);
+  state->file.size = ST_BUFSIZ;
+  state->file.count = 0;
+  state->file.pos = 0;
+  state->file.eof = 0;
 
-           ft->signal.size     = ST_SIZE_WORD;
-       ft->signal.encoding = ST_ENCODING_OKI_ADPCM;
-       ft->signal.channels = 1;
+  ft->signal.size = ST_SIZE_WORD;
+  ft->signal.encoding = ST_ENCODING_OKI_ADPCM;
+  ft->signal.channels = 1;
 
-       /* ... initialise CODEC state */
+  adpcm_init(&state->encoder, 1);
 
-       state->state.last  = 0;
-       state->state.index = 0;
-       state->store.byte  = 0;
-       state->store.flag  = 0;
+  state->store.byte = 0;
+  state->store.flag = 0;
 
-           return (ST_SUCCESS);
-    }
+  return (ST_SUCCESS);
+}
 
 /******************************************************************************
  * Function   : st_voxwrite
@@ -235,44 +186,44 @@
  * Notes      : 
  ******************************************************************************/
 
-static st_size_t st_voxwrite (ft_t ft,const st_sample_t *buffer,st_size_t length) 
-           { vox_t    state = (vox_t) ft->priv;
-             st_size_t count = 0;
-             uint8_t  byte  = state->store.byte;
-             uint8_t  flag  = state->store.flag;
-             short    word;
+static st_size_t st_voxwrite(ft_t ft, const st_sample_t * buffer, st_size_t length)
+{
+  vox_t state = (vox_t) ft->priv;
+  st_size_t count = 0;
+  uint8_t byte = state->store.byte;
+  uint8_t flag = state->store.flag;
+  short word;
 
-             while (count < length)
-                   { word   = ST_SAMPLE_TO_SIGNED_WORD (*buffer++, ft->clippedCount);
-                     word  /= 16;
+  while (count < length) {
+    word = ST_SAMPLE_TO_SIGNED_WORD(*buffer++, ft->clippedCount);
 
-                     byte <<= 4;
-                     byte  |= envox (word,state) & 0x0F;
+    byte <<= 4;
+    byte |= adpcm_encode(word, &state->encoder) & 0x0F;
 
-                     flag++;
-                     flag %= 2;
+    flag++;
+    flag %= 2;
 
-                     if (flag == 0)
-                        { state->file.buf[state->file.count++] = byte;
+    if (flag == 0) {
+      state->file.buf[state->file.count++] = byte;
 
-                          if (state->file.count >= state->file.size)
-                             { st_writebuf (ft,state->file.buf,1,state->file.count);
+      if (state->file.count >= state->file.size) {
+        st_writebuf(ft, state->file.buf, 1, state->file.count);
 
-                               state->file.count = 0;
-                             }
-                        }
+        state->file.count = 0;
+      }
+    }
 
-                     count++;
-                   }
+    count++;
+  }
 
-             /* ... keep last byte across calls */
+  /* ... keep last byte across calls */
 
-             state->store.byte = byte;
-             state->store.flag = flag;
-        
-             return (count);
-           }
+  state->store.byte = byte;
+  state->store.flag = flag;
 
+  return (count);
+}
+
 /******************************************************************************
  * Function   : st_voxstopwrite
  * Description: Flushes any leftover samples and frees the internal buffer 
@@ -283,132 +234,29 @@
  * Notes      : 
  ******************************************************************************/
 
-static int  st_voxstopwrite (ft_t ft) 
-     { vox_t    state = (vox_t) ft->priv;
-       uint8_t  byte  = state->store.byte;
-       uint8_t  flag  = state->store.flag;
+static int st_voxstopwrite(ft_t ft)
+{
+  vox_t state = (vox_t) ft->priv;
+  uint8_t byte = state->store.byte;
+  uint8_t flag = state->store.flag;
 
-       /* ... flush remaining samples */
+  /* ... flush remaining samples */
 
-       if (flag != 0)
-          { byte <<= 4;
-            byte  |= envox (0,state) & 0x0F;
+  if (flag != 0) {
+    byte <<= 4;
+    byte |= adpcm_encode(0, &state->encoder) & 0x0F;
 
-            state->file.buf[state->file.count++] = byte;
-          }
+    state->file.buf[state->file.count++] = byte;
+  }
 
-       if (state->file.count > 0)
-          st_writebuf (ft,state->file.buf,1,state->file.count);
+  if (state->file.count > 0)
+    st_writebuf(ft, state->file.buf, 1, state->file.count);
 
-       free (state->file.buf);
-     
-       return (ST_SUCCESS);
-     }
+  free(state->file.buf);
 
-/******************************************************************************
- * Function   : envox
- * Description: Internal utility routine to encode 12 bit signed PCM to 
- *              OKI ADPCM code
- * Parameters : sample  - 12 bit linear PCM sample
- *              state   - CODEC state
- * Returns    : uint8_t - ADPCM nibble (in low order nibble)
- * Exceptions :
- * Notes      : 
- ******************************************************************************/
+  return (ST_SUCCESS);
+}
 
-static uint8_t envox (short sample,vox_t state)
-        { uint8_t code;
-          short   dn;
-          short   ss;
-
-          ss   = STEPSIZE[state->state.index];
-          code = 0x00;
-
-          if ((dn = sample - state->state.last) < 0)
-             { code = 0x08;
-               dn   = -dn;
-             }
-          
-          if (dn >= ss) 
-             { code = code | 0x04;
-               dn  -= ss;
-             }
-
-          if (dn >= ss/2)
-             { code = code | 0x02;
-               dn  -= ss/2;
-             }
-
-          if (dn >= ss/4)
-             { code = code | 0x01;
-             }
-
-          /* ... use decoder to set the estimate of last sample and
-             adjust the step index */
-    
-          state->state.last = devox (code,state);
-    
-          return (code);
-        }
-
-
-/******************************************************************************
- * Function   : devox
- * Description: Internal utility routine to decode OKI ADPCM 4-bit samples to 
- *              12-bit signed PCM.
- * Parameters : code   - ADPCM code (nibble)
- *              state  - CODEC state
- * Returns    : short  - 12 bit signed PCM sample
- * Exceptions :
- * Notes      : 
- ******************************************************************************/
-
-static short devox (uint8_t code,vox_t state) 
-      { short dn;
-        short ss;
-        short sample;
-
-        ss = STEPSIZE[state->state.index];
-        dn = ss/8;
-
-        if (code & 0x01)
-           dn += ss/4;
-    
-        if (code & 0x02)
-           dn += ss/2;
-    
-        if (code & 0x04)
-           dn += ss;
-
-        if (code & 0x08)
-           dn = -dn;
-
-        sample = state->state.last + dn;
-
-        /* ... clip to 12 bits */
-
-        if (sample > 2047)
-           sample = 2047;
-
-        if (sample < -2048)
-           sample = -2048;
-
-        /* ... adjust step size */
-
-        state->state.last   = sample;
-        state->state.index += STEPADJUST[code & 0x07];
-    
-        if (state->state.index < 0) 
-           state->state.index = 0;
-
-        if (state->state.index > 48) 
-           state->state.index = 48;
-
-        /* ... done */
-
-        return (sample);
-      }
-
 static const char *voxnames[] = {
   "vox",
   NULL
@@ -429,5 +277,5 @@
 
 const st_format_t *st_vox_format_fn(void)
 {
-    return &st_vox_format;
+  return &st_vox_format;
 }