shithub: sox

Download patch

ref: da6bd76bcbdbad47ce45df93d3e000b91bc85673
parent: eb34a52b9b0c0b82b889cbef287c174a1344df36
author: cbagwell <cbagwell>
date: Mon Nov 22 11:09:01 EST 1999

IMA ADPCM write support in wav files, speed up of adpcm codecs, and speed
improvements to raw.

--- a/Changelog
+++ b/Changelog
@@ -6,6 +6,12 @@
 
 sox-12.17
 ---------
+  o Sox can now write IMA_ADPCM compressed .wav,
+	  MSADPCM writing should be available soon.
+  o General changes to wav.c for writing additional wav formats.
+  o Speedups to adpcm read routines, new codex versions are
+	  now in ima_rw.c and adpcm.c.
+  o Speedups for raw.c, especially for gcc with glibc.
   o Fixed a segfault problem with ulaw/alaw conversion, where
     an out-of-range index into the tables could occur.
   o More detailed output from the stat effect.
--- a/Makefile.gcc
+++ b/Makefile.gcc
@@ -24,12 +24,12 @@
 MANDIR  = $(PREFIX)/man
 INCDIR	= $(PREFIX)/include
 
-SRCDIR	= sox-12.16
+SRCDIR	= sox-12.17
 
 ##############################################################################
 
-FOBJ	= 8svx.o aiff.o alsa.o au.o auto.o avr.o cdr.o cvsd.o dat.o \
-	  g721.o g723_24.o g723_40.o g72x.o gsm.o hcom.o maud.o oss.o raw.o \
+FOBJ	= 8svx.o adpcm.o aiff.o alsa.o au.o auto.o avr.o cdr.o cvsd.o dat.o \
+	  g721.o g723_24.o g723_40.o g72x.o gsm.o hcom.o ima_rw.c maud.o oss.o raw.o \
 	  sf.o smp.o sndrtool.o sunaudio.o tx16w.o voc.o wav.o wve.o
 
 EOBJ    = avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
@@ -86,6 +86,14 @@
 # its in stdlib.h.
 GETOPT_DEFINES	= -DHAVE_GETOPT_H
 
+# If your system has bswap_16(), bswap_32() in byteswap.h
+# (Such as the case with GNU libc 2.1)
+# then uncomment the following line to use these fast versions.
+BYTESWAP_DEFINES	= -DHAVE_BYTESWAP_H
+
+# If your system has rand() then uncomment the following line.
+RAND_DEFINES	= -DHAVE_RAND
+
 # Uncomment the following if your system does not have a built in
 # strerror().  This includes SunOS.
 #
@@ -178,8 +186,8 @@
 SOX_POST_LIBS	= $(GSM_POST_LIBS) $(MATHLIB)
 SOX_INCLUDES	= $(GSM_INCLUDES)
 SOX_DEFINES	= $(GSM_DEFINES) $(PLAYER_DEFINES) \
-  $(GETOPT_DEFINES) $(STRERR_DEFINES) $(MEMMOVE_DEFINES) \
-  $(DOS_DEFINES) $(NEXT_DEFINES) $(MISC_DEFINES)
+  $(GETOPT_DEFINES) $(BYTESWAP_DEFINES) $(RAND_DEFINES) $(STRERR_DEFINES) \
+	$(MEMMOVE_DEFINES) $(DOS_DEFINES) $(NEXT_DEFINES) $(MISC_DEFINES)
 
 CFLAGS  = $O $(SOX_DEFINES) $(SOX_INCLUDES)
 
--- a/configure
+++ b/configure
@@ -1347,7 +1347,7 @@
 CPPFLAGS="$ac_save_CPPFLAGS"
 
 
-for ac_hdr in getopt.h unistd.h malloc.h
+for ac_hdr in getopt.h unistd.h malloc.h byteswap.h
 do
 ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
@@ -1389,7 +1389,7 @@
 
 
 
-for ac_func in getopt strerror memmove
+for ac_func in getopt strerror memmove rand
 do
 echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
 echo "configure:1396: checking for $ac_func" >&5
@@ -1595,6 +1595,49 @@
 fi
 if test "$oss_dsp" = yes
 then
+	if test "$ac_cv_dev_oss_dsp" = ""
+	then
+		for ac_hdr in sys/soundcard.h machine/soundcard.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1605: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1610 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1615: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+ 
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+	fi
 	CFLAGS="$CFLAGS -DOSS_PLAYER"
 	NEED_OSS=1
 	PLAY_SUPPORT=1
@@ -1603,7 +1646,7 @@
 if test "$sun_audio" = auto
 then
 	echo $ac_n "checking whether /dev/audio is functional (SUN style)""... $ac_c" 1>&6
-echo "configure:1607: checking whether /dev/audio is functional (SUN style)" >&5
+echo "configure:1650: checking whether /dev/audio is functional (SUN style)" >&5
 if eval "test \"`echo '$''{'ac_cv_dev_sun_audio'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
@@ -1611,7 +1654,7 @@
   ac_cv_dev_sun_audio=no
 else
   cat > conftest.$ac_ext <<EOF
-#line 1615 "configure"
+#line 1658 "configure"
 #include "confdefs.h"
 
 			int
@@ -1627,7 +1670,7 @@
 				}
 			
 EOF
-if { (eval echo configure:1631: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+if { (eval echo configure:1674: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
 then
   ac_cv_dev_sun_audio=yes
 else
@@ -1647,17 +1690,17 @@
 	then
 		ac_safe=`echo "sys/audioio.h" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for sys/audioio.h""... $ac_c" 1>&6
-echo "configure:1651: checking for sys/audioio.h" >&5
+echo "configure:1694: checking for sys/audioio.h" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1656 "configure"
+#line 1699 "configure"
 #include "confdefs.h"
 #include <sys/audioio.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1661: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1704: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -1680,17 +1723,17 @@
 
 		ac_safe=`echo "sun/audioio.h" | sed 'y%./+-%__p_%'`
 echo $ac_n "checking for sun/audioio.h""... $ac_c" 1>&6
-echo "configure:1684: checking for sun/audioio.h" >&5
+echo "configure:1727: checking for sun/audioio.h" >&5
 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
   echo $ac_n "(cached) $ac_c" 1>&6
 else
   cat > conftest.$ac_ext <<EOF
-#line 1689 "configure"
+#line 1732 "configure"
 #include "confdefs.h"
 #include <sun/audioio.h>
 EOF
 ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
-{ (eval echo configure:1694: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+{ (eval echo configure:1737: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
 ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
 if test -z "$ac_err"; then
   rm -rf conftest*
@@ -1719,6 +1762,49 @@
 fi
 if test "$sun_audio" = yes
 then
+	if test "$ac_cv_dev_sun_audio" = ""
+	then
+		for ac_hdr in sys/audioio.h
+do
+ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'`
+echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6
+echo "configure:1772: checking for $ac_hdr" >&5
+if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then
+  echo $ac_n "(cached) $ac_c" 1>&6
+else
+  cat > conftest.$ac_ext <<EOF
+#line 1777 "configure"
+#include "confdefs.h"
+#include <$ac_hdr>
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1782: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=yes"
+else
+  echo "$ac_err" >&5
+  echo "configure: failed program was:" >&5
+  cat conftest.$ac_ext >&5
+  rm -rf conftest*
+  eval "ac_cv_header_$ac_safe=no"
+fi
+rm -f conftest*
+fi
+if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then
+  echo "$ac_t""yes" 1>&6
+    ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'`
+  cat >> confdefs.h <<EOF
+#define $ac_tr_hdr 1
+EOF
+ sun/audioio.h
+else
+  echo "$ac_t""no" 1>&6
+fi
+done
+
+	fi
 	CFLAGS="$CFLAGS -DSUNAUDIO_PLAYER"
 	NEED_SUNAU=1
 	PLAY_SUPPORT=1
--- a/configure.in
+++ b/configure.in
@@ -202,7 +202,7 @@
 then
 	if test "$ac_cv_dev_oss_dsp" = ""
 	then
-		AC_CHEKC_HEADERS(sys/soundcard.h machine/soundchare.h,)
+		AC_CHECK_HEADERS(sys/soundcard.h machine/soundcard.h,)
 	fi
 	CFLAGS="$CFLAGS -DOSS_PLAYER"
 	NEED_OSS=1
@@ -246,7 +246,7 @@
 then
 	if test "$ac_cv_dev_sun_audio" = ""
 	then
-		AC_CHEKC_HEADERS(sys/audioio.h, sun/audioio.h,)
+		AC_CHECK_HEADERS(sys/audioio.h, sun/audioio.h,)
 	fi
 	CFLAGS="$CFLAGS -DSUNAUDIO_PLAYER"
 	NEED_SUNAU=1
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -41,8 +41,8 @@
 
 # Objects.
 
-FOBJ	= 8svx.o aiff.o au.o auto.o avr.o cdr.o cvsd.o dat.o g721.o \
-	  g723_24.o g723_40.o g72x.o gsm.o hcom.o maud.o raw.o \
+FOBJ	= 8svx.o adpcm.o aiff.o au.o auto.o avr.o cdr.o cvsd.o dat.o g721.o \
+	  g723_24.o g723_40.o g72x.o gsm.o hcom.o ima_rw.o maud.o raw.o \
 	  sf.o smp.o sndrtool.o tx16w.o voc.o wav.o wve.o
 
 EOBJ	= avg.o band.o bandpass.o breject.o btrworth.o chorus.o compand.o \
--- /dev/null
+++ b/src/adpcm.c
@@ -1,0 +1,165 @@
+/*
+ * adpcm.c  codex functions for MS_ADPCM data
+ *          (hopefully) provides interoperability with
+ *          Microsoft's ADPCM format, but, as usual,
+ *          see LACK-OF-WARRANTY information below.
+ *
+ *      Copyright (C) 1999 Stanley J. Brooks <stabro@megsinet.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+/*
+ * November 20, 1999
+ *  specs I've seen are unclear about ADPCM supporting more than 2 channels,
+ *  but these routines support more channels in a manner which looks (IMHO)
+ *  like the most natural extension.
+ *
+ *  Remark: code still turbulent, encoding to be added RSN
+ */
+
+#include <sys/types.h>
+#include <math.h>
+#include <stdio.h>
+#include "adpcm.h"
+
+typedef struct MsState {
+	LONG  step;	/* step size */
+	short iCoef[2];
+} MsState_t;
+
+#define lsbshortldi(x,p) { (x)=((short)((int)(p)[0] + ((int)(p)[1]<<8))); (p) += 2; }
+
+/*
+ * Lookup tables for MS ADPCM format
+ */
+
+/* these are step-size adjust factors, where
+ * 1.0 is scaled to 0x100
+ */
+static LONG stepAdjustTable[] = {
+	230, 230, 230, 230, 307, 409, 512, 614,
+	768, 614, 512, 409, 307, 230, 230, 230
+};
+
+/* TODO : The first 7 iCoef sets are always hardcoded and must
+   appear in the actual WAVE file.  They should be read in
+   in case a sound program added extras to the list. */
+
+static short iCoef[7][2] = {
+			{ 256,   0},
+			{ 512,-256},
+			{   0,   0},
+			{ 192,  64},
+			{ 240,   0},
+			{ 460,-208},
+			{ 392,-232}
+};
+
+#if 0
+static LONG AdpcmDecode(LONG, MsState_t*, LONG, LONG)__attribute__((regparm(3)));
+#endif
+
+#ifdef __GNUC__
+inline
+#endif
+static LONG AdpcmDecode(c, state, sample1, sample2)
+LONG c, sample1, sample2;
+MsState_t *state;
+{
+	LONG predict;
+	LONG sample;
+	LONG step, step1;
+
+	/** Compute next step value **/
+	step = state->step;
+	step1 = (stepAdjustTable[c] * step) >> 8;
+	state->step = (step1 < 16)? 16:step1;
+
+	/** make linear prediction for next sample **/
+	predict =
+			((sample1 * state->iCoef[0]) +
+			 (sample2 * state->iCoef[1])) >> 8;
+	/** then add the code*step adjustment **/
+	c -= (c & 0x08) << 1;
+	sample = (c * step) + predict;
+
+	if (sample > 32767)
+		sample = 32767;
+	else if (sample < -32768)
+		sample = -32768;
+
+	return (sample);
+}
+
+/* AdpcmBlockExpandI() outputs interleaved samples into one output buffer */
+const char *AdpcmBlockExpandI(
+	int chans,          /* total channels             */
+	const u_char *ibuff,/* input buffer[blockAlign]   */
+	SAMPL *obuff,       /* output samples, n*chans    */
+	int n               /* samples to decode PER channel */
+)
+{
+	const u_char *ip;
+	int ch;
+	const char *errmsg = NULL;
+	MsState_t state[4];						/* One decompressor state for each channel */
+
+	/* Read the four-byte header for each channel */
+	ip = ibuff;
+	for (ch = 0; ch < chans; ch++) {
+		u_char bpred = *ip++;
+		if (bpred >= 7) {
+			errmsg = "MSADPCM bpred >= 7, arbitrarily using 0\n";
+			bpred = 0;
+		}
+		state[ch].iCoef[0] = iCoef[(int)bpred][0];
+		state[ch].iCoef[1] = iCoef[(int)bpred][1];
+	}
+
+	for (ch = 0; ch < chans; ch++)
+		lsbshortldi(state[ch].step, ip);
+
+	/* sample1's directly into obuff */
+	for (ch = 0; ch < chans; ch++)
+		lsbshortldi(obuff[chans+ch], ip);
+
+	/* sample2's directly into obuff */
+	for (ch = 0; ch < chans; ch++)
+		lsbshortldi(obuff[ch], ip);
+
+	{
+		int ch;
+		u_char b;
+		short *op, *top;
+
+		/* already have 1st 2 samples from block-header */
+		op = obuff + 2*chans;
+		top = obuff + n*chans;
+
+		ch = 0;
+		while (op < top) {
+			b = *ip++;
+			*op++ = AdpcmDecode(b >> 4, state+ch, op[-chans], op[-2*chans]);
+			if (++ch == chans) ch = 0;
+			/* ch = ++ch % chans; */
+			*op++ = AdpcmDecode(b&0x0f, state+ch, op[-chans], op[-2*chans]);
+			if (++ch == chans) ch = 0;
+			/* ch = ++ch % chans; */
+		}
+	}
+	return errmsg;
+}
--- /dev/null
+++ b/src/adpcm.h
@@ -1,0 +1,21 @@
+/*
+ * adpcm.h
+ */
+#ifndef SAMPL
+#define SAMPL short
+#endif
+#ifndef LONG
+#define LONG long
+#endif
+#ifndef ULONG
+#define ULONG u_long
+#endif
+
+/* AdpcmBlockExpandI() outputs interleaved samples into one output buffer */
+extern const char *AdpcmBlockExpandI(
+	int chans,          /* total channels             */
+	const u_char *ibuff,/* input buffer[blockAlign]   */
+	SAMPL *obuff,       /* output samples, n*chans    */
+	int n               /* samples to decode PER channel, REQUIRE n % 8 == 1  */
+);
+
--- a/src/filter.c
+++ b/src/filter.c
@@ -3,11 +3,36 @@
  */
 
 /*
- * November 7, 1999
+ * November 18, 1999
+ * Copyright 1994 Julius O. Smith
+ * Copyright 1991 (?) Lance Norskog (?)
+ * Copyright 1999 Stan Brooks <stabro@megsinet.net>
  *
+ * -------------------------------------------------------------------
  * This source code 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.
+ * -------------------------------------------------------------------
+ *
+ * REMARKS: (Stan Brooks speaking)
+ * This code is heavily based on the resample.c code which was
+ * apparently itself a rewrite (by Lance Norskog?) of code originally
+ * by Julius O. Smith, and distributed under the GPL license...
+ * I am currently trying to contact Lance Norskog and Julius O. Smith
+ * to hopefully clear up the question of copyright, license, etc, of
+ * this code and resample.c. The LGPL license would be ideal IMHO.
+ *
+ * The following was in the README file accompanying resample-1.2 :
+ *
+ *   COPYING
+ *
+ *   This software package is Copyright 1994 by Julius O. Smith
+ *   (jos@ccrma.stanford.edu), all rights reserved.  Permission to use and copy
+ *   is granted subject to the terms of the "GNU Software General Public
+ *   License" (see ftp://prep.ai.mit.edu/pub/gnu/COPYING).  In addition, we
+ *   request that a copy of any modified files be sent by email to
+ *   jos@ccrma.stanford.edu so that we may incorporate them in the CCRMA
+ *   version.
  *
  */
 
--- /dev/null
+++ b/src/ima_rw.c
@@ -1,0 +1,405 @@
+/*
+	ima_rw.c -- codex utilities for WAV_FORMAT_IMA_ADPCM
+	 
+	Copyright (C) 1999 Stanley J. Brooks <stabro@megsinet.net> 
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+*/
+
+#include <sys/types.h>
+#include <math.h>
+#include <stdio.h>
+#include "ima_rw.h"
+
+/*
+ *
+ * Lookup tables for IMA ADPCM format
+ *
+ */
+#define ISSTMAX 88
+
+static const int imaStepSizeTable[ISSTMAX + 1] = {
+	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
+};
+
+#define imaStateAdjust(c) (((c)<4)? -1:(2*(c)-6))
+/* +0 - +3, decrease step size */
+/* +4 - +7, increase step size */
+/* -0 - -3, decrease step size */
+/* -4 - -7, increase step size */
+
+static u_char imaStateAdjustTable[ISSTMAX+1][8];
+
+void initImaTable(void)
+{
+	int i,j,k;
+	for (i=0; i<=ISSTMAX; i++) {
+		for (j=0; j<8; j++) {
+			k = i + imaStateAdjust(j);
+			if (k<0) k=0;
+			else if (k>ISSTMAX) k=ISSTMAX;
+			imaStateAdjustTable[i][j] = k;
+		}
+	}
+}
+
+static void ImaExpandS(
+	int ch,             /* channel number to decode, REQUIRE 0 <= ch < chans  */
+	int chans,          /* total channels             */
+	const u_char *ibuff,/* input buffer[blockAlign]   */
+	SAMPL *obuff,       /* obuff[n] will be output samples */
+	int n,              /* samples to decode PER channel, REQUIRE n % 8 == 1  */
+	int o_inc           /* index difference between successive output samples */
+)
+{
+	const u_char *ip;
+	int i_inc;
+	SAMPL *op;
+	int i, val, state;
+
+	ip = ibuff + 4*ch;     /* input pointer to 4-byte block state-initializer   */
+	/* fprintf(stderr, "IMA_ADPCM ip %p op %p\n", ip,obuff); */
+	i_inc = 4*(chans-1);   /* amount by which to incr ip after each 4-byte read */
+	val = (short)(ip[0] + (ip[1]<<8)); /* need cast for sign-extend */
+	state = ip[2];
+	if (state > ISSTMAX) {
+		fprintf(stderr, "IMA_ADPCM block ch%d initial-state (%d) out of range\n", ch, state);
+		fflush(stderr);
+		state = 0;
+	}
+	/* specs say to ignore ip[3] , but write it as 0 */
+	ip += 4+i_inc;
+
+	op = obuff;
+	*op = val;      /* 1st output sample for this channel */
+	op += o_inc;
+	/* fprintf(stderr, "ch%d val000 .... %.4x %2d\n", ch,val,state); */
+
+	for (i = 1; i < n; i++) {
+		int step,dp,c,cm;
+
+		if (i&1) {         /* 1st of pair */
+			cm = *ip & 0x0f;
+		} else {
+			cm = (*ip++)>>4;
+			if ((i&7) == 0)  /* ends the 8-sample input block for this channel */
+				ip += i_inc;   /* skip ip for next group */ 
+		}
+
+		step = imaStepSizeTable[state];
+		/* Update the state for the next sample */
+		c = cm & 0x07;
+		state = imaStateAdjustTable[state][c];
+
+#		ifdef STRICT_IMA
+		dp = 0;
+		if (c & 4) dp += step;
+		step = step >> 1;
+		if (c & 2) dp += step;
+		step = step >> 1;
+		if (c & 1) dp += step;
+		step = step >> 1;
+		dp += step;
+#		else
+		dp = ((c+c+1) * step) >> 3; /* faster than bit-test & add on my cpu */
+#		endif
+		if (c != cm) {
+			val -= dp;
+			if (val<-0x8000) val = -0x8000;
+		} else {
+			val += dp;
+			if (val>0x7fff) val = 0x7fff;
+		}
+		*op = val;
+		op += o_inc;
+		/* fprintf(stderr, "ch%d val%3d %.4x %.4x %2d\n", ch,i,dp,val,state); */
+	}
+	/* fprintf(stderr, " -> ip %p op %p\n", ip,op); */
+	return;
+}
+
+/* ImaBlockExpandI() outputs interleaved samples into one output buffer */
+void ImaBlockExpandI(
+	int chans,          /* total channels             */
+	const u_char *ibuff,/* input buffer[blockAlign]   */
+	SAMPL *obuff,       /* output samples, n*chans    */
+	int n               /* samples to decode PER channel, REQUIRE n % 8 == 1  */
+)
+{
+	int ch;
+	for (ch=0; ch<chans; ch++)
+		ImaExpandS(ch, chans, ibuff, obuff+ch, n, chans);
+}
+
+/* ImaBlockExpandM() outputs non-interleaved samples into chan separate output buffers */
+void ImaBlockExpandM(
+	int chans,          /* total channels             */
+	const u_char *ibuff,/* input buffer[blockAlign]   */
+	SAMPL **obuffs,     /* chan output sample buffers, each takes n samples */
+	int n               /* samples to decode PER channel, REQUIRE n % 8 == 1  */
+)
+{
+	int ch;
+	for (ch=0; ch<chans; ch++)
+		ImaExpandS(ch, chans, ibuff, obuffs[ch], n, 1);
+}
+
+static int ImaMashS(
+	int ch,             /* channel number to encode, REQUIRE 0 <= ch < chans  */
+	int chans,          /* total channels */
+	SAMPL v0,           /* value to use as starting prediction0 */
+	const SAMPL *ibuff, /* ibuff[] is interleaved input samples */
+	int n,              /* samples to encode PER channel, REQUIRE n % 8 == 1 */
+	int *st,            /* input/output state, REQUIRE 0 <= *st <= ISSTMAX */
+	u_char *obuff,      /* output buffer[blockAlign], or NULL for no output  */
+	int sho             /* nonzero for debug printout */
+)
+{
+	const SAMPL *ip, *itop;
+	u_char *op;
+	int o_inc = 0;      /* set 0 only to shut up gcc's 'might be uninitialized' */
+	int i, val;
+	int state;
+	double d2;  /* long long is okay also, speed abt the same */
+
+	ip = ibuff + ch;       /* point ip to 1st input sample for this channel */
+	itop = ibuff + n*chans;
+	val = *ip - v0; ip += chans;/* 1st input sample for this channel */
+	d2 = val*val;/* d2 will be sum of squares of errors, given input v0 and *st */
+	val = v0;	
+
+	op = obuff;            /* output pointer (or NULL) */
+	if (op) {              /* NULL means don't output, just compute the rms error */
+		op += 4*ch;          /* where to put this channel's 4-byte block state-initializer */
+		o_inc = 4*(chans-1); /* amount by which to incr op after each 4-byte written */
+		*op++ = val; *op++ = val>>8;
+		*op++ = *st; *op++ = 0; /* they could have put a mid-block state-correction here  */
+		op += o_inc;            /* _sigh_   NEVER waste a byte.      It's a rule!         */
+	}
+
+	state = *st;
+
+	for (i = 0; ip < itop; ip+=chans) {
+		int step,d,dp,c;
+
+		d = *ip - val;  /* difference between last prediction and current sample */
+
+		step = imaStepSizeTable[state];
+		c = (abs(d)<<2)/step;
+		if (c > 7) c = 7;
+		/* Update the state for the next sample */
+		state = imaStateAdjustTable[state][c];
+
+		if (op) {   /* if we want output, put it in proper place */
+			int cm = c;
+			if (d<0) cm |= 8;
+			if (i&1) {       /* odd numbered output */
+				*op++ |= (cm<<4);
+				if (i == 7)    /* ends the 8-sample output block for this channel */
+					op += o_inc; /* skip op for next group */ 
+			} else {
+				*op = cm;
+			}
+			i = (i+1) & 0x07;
+		}
+
+#		ifdef STRICT_IMA
+		dp = 0;
+		if (c & 4) dp += step;
+		step = step >> 1;
+		if (c & 2) dp += step;
+		step = step >> 1;
+		if (c & 1) dp += step;
+		step = step >> 1;
+		dp += step;
+#		else
+		dp = ((c+c+1) * step) >> 3; /* faster than bit-test & add on my cpu */
+#		endif
+		if (d<0) {
+			val -= dp;
+			if (val<-0x8000) val = -0x8000;
+		} else {
+			val += dp;
+			if (val>0x7fff) val = 0x7fff;
+		}
+
+		{
+			int x = *ip - val;
+			d2 += x*x;
+		}
+
+	}
+	d2 /= n; /* be sure it's non-negative */
+	if (sho) {
+		fprintf(stderr, "n %d, st %d->%d, d %.1f\n", n, *st, state, sqrt(d2));
+		fflush(stderr);
+	}
+	*st = state;
+	return (int) sqrt(d2);
+}
+
+#if 0
+static long AvgDelta(int ch, int chans, const SAMPL *ibuff, int n)
+{
+	const SAMPL *ip, *itop;
+	int v0;
+	long d1;
+	
+	ip = ibuff + ch;
+	itop = ip + n*chans;
+	d1 = 0;
+	v0 = *ip;
+	ip += chans;
+	for ( ; ip < itop; ip+=chans) {
+		int v1;
+
+		v1 = *ip;
+		d1 += abs(v1-v0);
+		v0 = v1;
+	}
+	return (d1/(n-1));
+}
+#endif
+
+/* mash one channel... if you want to use opt>0, 9 is a reasonable value */
+void ImaMashChannel(
+	int ch,             /* channel number to encode, REQUIRE 0 <= ch < chans  */
+	int chans,          /* total channels */
+	const SAMPL *ip,    /* ip[] is interleaved input samples */
+	int n,              /* samples to encode PER channel, REQUIRE n % 8 == 1 */
+	int *st,            /* input/output state, REQUIRE 0 <= *st <= ISSTMAX */
+	u_char *obuff,      /* output buffer[blockAlign] */
+	int opt             /* non-zero allows some cpu-intensive code to improve output */
+)
+{
+	int snext,d;
+	int s0,d0;
+	int s32,d32;
+	int sho = 0;
+
+	s32 = s0 = *st;
+	if (opt>0) {
+		int low,hi,w;
+		int low0,hi0;
+		snext = s0;
+		d32 = d0 = ImaMashS(ch, chans, ip[0], ip,n,&snext, NULL, sho);
+
+		w = 0;
+		low=hi=s0;
+		low0 = low-opt; if (low0<0) low0=0;
+		hi0 = hi+opt; if (hi0>ISSTMAX) hi0=ISSTMAX;
+		while (low>low0 || hi<hi0) {
+			if (!w && low>low0) {
+				int d;
+				snext = --low;
+				d = ImaMashS(ch, chans, ip[0], ip,n,&snext, NULL, sho);
+				if (d<d0) {
+					d0=d; s0=low;
+					low0 = low-opt; if (low0<0) low0=0;
+					hi0 = low+opt; if (hi0>ISSTMAX) hi0=ISSTMAX;
+				}
+			}
+			if (w && hi<hi0) {
+				int d;
+				snext = ++hi;
+				d = ImaMashS(ch, chans, ip[0], ip,n,&snext, NULL, sho);
+				if (d<d0) {
+					d0=d; s0=hi;
+					low0 = hi-opt; if (low0<0) low0=0;
+					hi0 = hi+opt; if (hi0>ISSTMAX) hi0=ISSTMAX;
+				}
+			}
+			w=1-w;
+		}
+		*st = s0;
+	}
+	d = ImaMashS(ch, chans, ip[0], ip,n,st, obuff, 0);
+	/* printf("%4d %6d %6d\n", s0-s32, d0, d32-d0); */
+	/* printf("%5d %2d\n", AvgDelta(ch,O.chans,ip,32), s0); */
+}
+
+#if 0
+static void ImaMashChannel(int ch, const SAMPL *ip, int n, int *st)
+{
+	int s,snext,d;
+	int s0,d0;
+	int s32,d32;
+	int sho = 0;
+	int mx[ISSTMAX+1];
+
+#if 0
+	s32=-1; d32=0x10000;
+	for (s=3; s<=ISSTMAX; s += 9) {
+		snext = s;
+		d = ImaMashS(ch, O.chans, ip[0], ip,n,&snext, NULL, sho);
+		if (d<d32) {
+			d32=d; s32=s;
+		}
+	}
+#endif
+	s0=-1; d0=0x10000;
+	for (s=0; s<=ISSTMAX;s++) {
+		snext = s;
+		mx[s] = d = ImaMashS(ch, O.chans, ip[0], ip,n,&snext, NULL, sho);
+		/* if (s==s32) d32 = d; */
+		if (d<d0) {
+			d0=d; s0=s;
+		}
+	}
+	s32 = *st;
+	d32 = mx[s32];
+#if 1
+	{
+		int low,hi,w;
+		int low0,hi0;
+		w = 0;
+		low=hi=s32;
+		d32 = mx[s32];
+		low0 = low-9; if (low0<0) low0=0;
+		hi0 = hi+9; if (hi0>ISSTMAX) hi0=ISSTMAX;
+		while (low>low0 || hi<hi0) {
+			if (!w && low>low0) {
+				if (mx[--low]<d32) {
+					d32=mx[low]; s32=low;
+					low0 = low-9; if (low0<0) low0=0;
+					hi0 = low+9; if (hi0>ISSTMAX) hi0=ISSTMAX;
+				}
+			}
+			if (w && hi<hi0) {
+				if (mx[++hi]<d32) {
+					d32=mx[hi]; s32=hi;
+					low0 = hi-9; if (low0<0) low0=0;
+					hi0 = hi+9; if (hi0>ISSTMAX) hi0=ISSTMAX;
+				}
+			}
+			w=1-w;
+		}
+	}
+#endif
+	*st = s0;
+	d = ImaMashS(ch, O.chans, ip[0], ip,n,st, O.packet, 0);
+	printf("%4d %6d %6d\n", s0-s32, d0, d32-d0);
+	/* printf("%5d %2d\n", AvgDelta(ch,O.chans,ip,32), s0); */
+}
+#endif
+
--- /dev/null
+++ b/src/ima_rw.h
@@ -1,0 +1,66 @@
+/*
+	ima_rw.h -- codex utilities for WAV_FORMAT_IMA_ADPCM
+	 
+	Copyright (C) 1999 Stanley J. Brooks <stabro@megsinet.net> 
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ 
+*/
+#ifndef SAMPL
+#	define SAMPL short
+#endif
+
+/* #undef STRICT_IMA makes code a bit faster, but not
+ * strictly compatible with the real IMA spec, which
+ * defines an encoding on approximate multiply/divide
+ * using shift/adds instead of the (now) faster mult/div
+ * Probably the roundof inexactness is inaudibly small,
+ * so you might consider it worthwhile to #undef STRICT_IMA
+ */
+#define STRICT_IMA
+/* #undef STRICT_IMA */
+
+/*
+ * call initImaTable() before any other Ima* functions,
+ * to create the fast lookup tables
+ */
+extern void initImaTable(void);
+
+/* ImaBlockExpandI() outputs interleaved samples into one output buffer */
+extern void ImaBlockExpandI(
+	int chans,          /* total channels             */
+	const u_char *ibuff,/* input buffer[blockAlign]   */
+	SAMPL *obuff,       /* output samples, n*chans    */
+	int n               /* samples to decode PER channel, REQUIRE n % 8 == 1  */
+);
+
+/* ImaBlockExpandM() outputs non-interleaved samples into chan separate output buffers */
+extern void ImaBlockExpandM(
+	int chans,          /* total channels             */
+	const u_char *ibuff,/* input buffer[blockAlign]   */
+	SAMPL **obuffs,     /* chan output sample buffers, each takes n samples */
+	int n               /* samples to decode PER channel, REQUIRE n % 8 == 1  */
+);
+
+extern void ImaMashChannel(
+	int ch,             /* channel number to encode, REQUIRE 0 <= ch < chans  */
+	int chans,          /* total channels */
+	const SAMPL *ip,    /* ip[] is interleaved input samples */
+	int n,              /* samples to encode PER channel, REQUIRE n % 8 == 1 */
+	int *st,            /* input/output state, REQUIRE 0 <= *st <= ISSTMAX */
+	u_char *obuff,      /* output buffer[blockAlign] */
+	int opt             /* non-zero allows some cpu-intensive code to improve output */
+);
+
--- a/src/misc.c
+++ b/src/misc.c
@@ -15,6 +15,7 @@
 #include "version.h"
 #include "patchlvl.h"
 #include <stdio.h>
+#include <stdlib.h>
 #include <time.h>
 
 char *sizes[] = {
@@ -170,8 +171,6 @@
 	return ((us >> 8) | (us << 8)) & 0xffff;
 }
 
-#endif
-
 ULONG
 swapl(ul)
 ULONG ul;
@@ -192,6 +191,8 @@
 	u.l= (u.l>>24) | ((u.l>>8)&0xff00) | ((u.l<<8)&0xff0000L) | (u.l<<24);
 	return u.f;
 }
+
+#endif
 
 double
 swapd(df)
--- a/src/polyphas.c
+++ b/src/polyphas.c
@@ -2,19 +2,25 @@
  * July 14, 1998
  * Copyright 1998  K. Bradley, Carnegie Mellon University
  *
- * This source code is freely redistributable and may be used for
- * any purpose.  This copyright notice must be maintained. 
- * Lance Norskog And Sundry Contributors are not responsible for 
- * the consequences of using this software.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
  */
 
 /*
  * October 29, 1999
  * Various changes, bugfixes, speedups, by Stan Brooks.
- *
- * This source code 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.
  *
  */
 
--- a/src/raw.c
+++ b/src/raw.c
@@ -33,6 +33,8 @@
 #define memmove(dest, src, len) bcopy((src), (dest), (len))
 #endif
 
+#define MAXWSPEED 1
+
 void rawstartread(ft) 
 ft_t ft;
 {
@@ -127,6 +129,24 @@
 		swapn(p,n);
 }
 
+static int blockr_sw(p0, n, ft)
+LONG *p0,n;
+ft_t ft;
+{
+    LONG x, done;
+		short s;
+
+		for (done=0; done < n;) {
+			blockr(&s, sizeof(short), ft);
+			x = s;
+			if (ft->file.eof) break;
+			/* scale signed up to long's range */
+			*p0++ = LEFT(x, 16);
+			done++;
+		}
+	return done;
+}
+
 LONG rawread(ft, buf, nsamp) 
 ft_t ft;
 LONG *buf, nsamp;
@@ -189,28 +209,19 @@
 		    switch(ft->info.style)
 		    {
 			case SIGN2:
-				while(done < nsamp) {
-					short s;
-					blockr(&s, sizeof(short), ft);
-					datum = s;
-					if (ft->file.eof)
-						return done;
-					/* scale signed up to long's range */
-					*buf++ = LEFT(datum, 16);
-					done++;
-				}
-				return done;
+				return blockr_sw(buf, nsamp, ft);
 			case UNSIGNED:
 				while(done < nsamp) {
+					LONG x;
 					unsigned short s;
 					blockr(&s, sizeof(short), ft);
-					datum = s;
+					x = s;
 					if (ft->file.eof)
 						return done;
 					/* Convert to signed */
-					datum ^= 0x8000;
+					x ^= 0x8000;
 					/* scale signed up to long's range */
-					*buf++ = LEFT(datum, 16);
+					*buf++ = LEFT(x, 16);
 					done++;
 				}
 				return done;
@@ -296,6 +307,57 @@
 	ft->file.pos += n;
 }
 
+static void blockw_sw(ft, buf, nsamp)
+ft_t ft;
+LONG *buf, nsamp;
+{
+	short *top;
+
+	top = (short*)(ft->file.buf + ft->file.size);
+	while (nsamp) {
+		short *p, *q;
+		p = (short*)(ft->file.buf + ft->file.pos);
+		if (p >= top) {
+			blockflush(ft);
+			continue;
+		}
+		q = p+nsamp; if (q>top) q = top;
+		ft->file.pos += (q-p)*sizeof(short);
+		nsamp -= (q-p);
+		if (ft->swap) {
+#	ifdef MAXWSPEED
+			q -= 4;
+			while (p<q) {
+				p[0] = swapw((buf[0] + 0x8000) >> 16); /* round for 16 bit */
+				p[1] = swapw((buf[1] + 0x8000) >> 16); /* round for 16 bit */
+				p[2] = swapw((buf[2] + 0x8000) >> 16); /* round for 16 bit */
+				p[3] = swapw((buf[3] + 0x8000) >> 16); /* round for 16 bit */
+				p += 4; buf += 4;
+			}
+			q += 4;
+#	endif
+			while (p<q) {
+				*p++ = swapw(((*buf++) + 0x8000) >> 16); /* round for 16 bit */
+			}
+		} else {
+#	ifdef MAXWSPEED
+			q -= 4;
+			while (p<q) {
+				p[0] = (buf[0] + 0x8000) >> 16; /* round for 16 bit */
+				p[1] = (buf[1] + 0x8000) >> 16; /* round for 16 bit */
+				p[2] = (buf[2] + 0x8000) >> 16; /* round for 16 bit */
+				p[3] = (buf[3] + 0x8000) >> 16; /* round for 16 bit */
+				p += 4; buf += 4;
+			}
+			q += 4;
+#	endif
+			while (p<q) {
+				*p++ = ((*buf++) + 0x8000) >> 16; /* round for 16 bit */
+			}
+		}
+	}
+}
+
 /* Convert the sox internal signed long format */
 /* to the raw file data, and write it. */
 
@@ -356,14 +418,7 @@
 		    switch(ft->info.style)
 		    {
 			case SIGN2:
-				while(done < nsamp) {
-					short s;
-					/* scale signed up to long's range */
-					datum = *buf++ + 0x8000; /* round for 16 bit */
-					s = RIGHT(datum, 16);
-					blockw(&s, sizeof(short),ft);
-					done++;
-				}
+				blockw_sw(ft,buf,nsamp);
 				return;
 			case UNSIGNED:
 				while(done < nsamp) {
--- a/src/resample.c
+++ b/src/resample.c
@@ -48,12 +48,14 @@
 /* resample includes */
 #include "resampl.h"
 
+/* this Float MUST match that in filter.c */
 #define Float double/*float*/
 #define ISCALE 0x10000
 
+/* largest factor for which exact-coefficients upsampling will be used */
 #define NQMAX 511
-#define BUFFSIZE 8192 /*16384*/               /* Total buffer size */
-#define L64 long long
+
+#define BUFFSIZE 8192 /*16384*/  /* Total I/O buffer size */
 
 /* Private data for Lerp via LCM file */
 typedef struct resamplestuff {
--- a/src/sox.c
+++ b/src/sox.c
@@ -234,7 +234,13 @@
 			if (ft->filetype[0] == '.')
 				ft->filetype++;
 			break;
-
+#if 0
+		case 'X':  /* extract a subinterval of samples as input */
+			if (! ft) usage("-X");
+			ft->fileextract++;
+			ft->filetype = optarg;
+			break;
+#endif
 		case 'r':
 			if (! ft) usage("-r");
 			str = optarg;
--- a/src/stat.c
+++ b/src/stat.c
@@ -22,12 +22,14 @@
 
 /* Private data for STAT effect */
 typedef struct statstuff {
-	double	min, max, asum, sum1, sum2;		/* amplitudes */
-	double	dmin, dmax, dsum1, dsum2;	/* deltas */
-	double	scale;			/* scale-factor    */
-	double	last;			/* previous sample */
-	double	read;
-	int	first;
+	double	min, max;
+	double	asum;
+	double	sum1, sum2;	/* amplitudes */
+	double	dmin, dmax;
+	double	dsum1, dsum2;	/* deltas */
+	double	scale;		/* scale-factor    */
+	double	last;		/* previous sample */
+	ULONG	read;		/* samples processed */
 	int	volume;
 	int	srms;
 	ULONG   bin[4];
@@ -94,7 +96,6 @@
 	stat_t stat = (stat_t) effp->priv;
 	int i;
 
-	stat->first = 1;
 	stat->min = stat->max = 0;
 	stat->asum = 0;
 	stat->sum1 = stat->sum2 = 0;
@@ -102,6 +103,7 @@
 	stat->dmin = stat->dmax = 0;
 	stat->dsum1 = stat->dsum2 = 0;
 
+	stat->last = 0;
 	stat->read = 0;
 
 	for (i = 0; i < 4; i++)
@@ -121,16 +123,23 @@
 {
 	stat_t stat = (stat_t) effp->priv;
 	int len, done;
-	double samp, delta;
 	short count;
 
 	count = 0;
 	len = ((*isamp > *osamp) ? *osamp : *isamp);
+	if (len==0) return;
+
+	if (stat->read == 0)	/* 1st sample */
+		stat->min = stat->max = stat->last = (*ibuf)/stat->scale;
+
 	for(done = 0; done < len; done++) {
-		/* work in absolute levels for both sample and delta */
-		samp = (*ibuf)/stat->scale;
-    stat->bin[RIGHT(*ibuf,30)+2]++;
-		*obuf++ = *ibuf++;
+		long lsamp;
+		double samp, delta;
+		/* work in scaled levels for both sample and delta */
+		lsamp = *ibuf++;
+		samp = (double)lsamp/stat->scale;
+		stat->bin[RIGHT(lsamp,30)+2]++;
+		*obuf++ = lsamp;
 
 		if (stat->volume == 2)
 		{
@@ -137,16 +146,12 @@
 		    fprintf(stderr,"%f ",samp);
 		    if (count++ == 5)
 		    {
-				fprintf(stderr,"\n");
-				count = 0;
+			fprintf(stderr,"\n");
+			count = 0;
 		    }
 		}
 
-
-		if (stat->first) {
-			stat->min = stat->max = samp;
-			stat->first = 0;
-		}
+		/* update min/max */
 		if (stat->min > samp)
 			stat->min = samp;
 		else if (stat->max < samp)
@@ -180,15 +185,15 @@
 eff_t effp;
 {
 	stat_t stat = (stat_t) effp->priv;
-	double amp, scale, srms = 0, freq;
+	double amp, scale, rms = 0, freq;
 	double x, ct;
 
 	ct = stat->read;
 
-	if (stat->srms) {
+	if (stat->srms) {  /* adjust results to units of rms */
 		double f;
-		srms = sqrt(stat->sum2/ct);
-		f = 1.0/srms;
+		rms = sqrt(stat->sum2/ct);
+		f = 1.0/rms;
 		stat->max *= f;
 		stat->min *= f;
 		stat->asum *= f;
@@ -198,7 +203,7 @@
 		stat->dmin *= f;
 		stat->dsum1 *= f;
 		stat->dsum2 *= f*f;
-		stat->scale *= srms;
+		stat->scale *= rms;
 	}
 
 	scale = stat->scale;
@@ -215,10 +220,11 @@
 	if (stat->volume == 2) {
 		fprintf(stderr, "\n");
 	}
-	/* print them out */
-	fprintf(stderr, "Samples read:      %12lu\n", (unsigned long)ct);
+	/* print out the info */
+	fprintf(stderr, "Samples read:      %12lu\n", stat->read);
+	fprintf(stderr, "Length (seconds):  %12.6f\n", (double)stat->read/effp->ininfo.rate);
 	if (stat->srms)
-		fprintf(stderr, "Scaled by rms:     %12.6f\n", srms);
+		fprintf(stderr, "Scaled by rms:     %12.6f\n", rms);
 	else
 		fprintf(stderr, "Scaled by:         %12.1f\n", scale);
 	fprintf(stderr, "Maximum amplitude: %12.6f\n", stat->max);
@@ -229,8 +235,8 @@
 
 	fprintf(stderr, "Maximum delta:     %12.6f\n", stat->dmax);
 	fprintf(stderr, "Minimum delta:     %12.6f\n", stat->dmin);
-	fprintf(stderr, "Mean    delta:     %12.6f\n", stat->dsum1/ct);
-	fprintf(stderr, "RMS     delta:     %12.6f\n", sqrt(stat->dsum2/ct));
+	fprintf(stderr, "Mean    delta:     %12.6f\n", stat->dsum1/(ct-1));
+	fprintf(stderr, "RMS     delta:     %12.6f\n", sqrt(stat->dsum2/(ct-1)));
 	freq = sqrt(stat->dsum2/stat->sum2)*effp->ininfo.rate/(M_PI*2);
 	fprintf(stderr, "Rough   frequency: %12d\n", (int)freq);
 
--- a/src/wav.c
+++ b/src/wav.c
@@ -8,6 +8,13 @@
  *
  * Change History:
  *
+ * November  11, 1999 - Stan Brooks (stabro@megsinet.com)
+ *   Mods for faster adpcm decoding and addition of ima_adpcm
+ *   writing... low-level codex functions moved to external
+ *   modules ima_rw.c and adpcm.c. Some general cleanup, consistent
+ *   with writing *adpcm and other output formats.
+ *   Headers for adpcm include the 'fact' subchunk.
+ *
  * September 11, 1998 - Chris Bagwell (cbagwell@sprynet.com)
  *   Fixed length bug for IMA and MS ADPCM files.
  *
@@ -70,11 +77,13 @@
 
 #include "st.h"
 #include "wav.h"
+#include "ima_rw.h"
+#include "adpcm.h"
 
 /* Private data for .wav file */
 typedef struct wavstuff {
     LONG	   numSamples;
-    int		   second_header;  /* non-zero on second header write */
+    LONG	   dataLength;     /* needed for ADPCM writing */
     unsigned short formatTag;	   /* What type of encoding file is using */
     
     /* The following are only needed for ADPCM wav files */
@@ -81,128 +90,39 @@
     unsigned short samplesPerBlock;
     unsigned short bytesPerBlock;
     unsigned short blockAlign;
-    short	  *samples[2];	    /* Left and Right sample buffers */
-    short	  *samplePtr[2];    /* Pointers to current samples */
-    unsigned short blockSamplesRemaining;/* Samples remaining in each channel */    
+    unsigned short nCoefs;	    /* ADPCM: number of coef sets */
+    short	  *iCoefs;	    /* ADPCM: coef sets           */
     unsigned char *packet;	    /* Temporary buffer for packets */
+    short	  *samples;	    /* interleaved samples buffer */
+    short	  *samplePtr;       /* Pointer to current samples */
+    short	  *sampleTop;       /* End of samples-buffer      */
+    unsigned short blockSamplesRemaining;/* Samples remaining in each channel */    
+    int 	   state[16];       /* last, because maybe longer */
 } *wav_t;
 
 static char *wav_format_str();
 
-void wavwritehdr(P1(ft_t));
+LONG rawread(P3(ft_t, LONG *, LONG));
+void rawwrite(P3(ft_t, LONG *, LONG));
+void wavwritehdr(P2(ft_t, int));
 
-
-/*
- *
- * Lookup tables for MS ADPCM format
- *
- */
-
-static LONG gaiP4[]    = { 230, 230, 230, 230, 307, 409, 512, 614,
-			   768, 614, 512, 409, 307, 230, 230, 230 };
-
-/* TODO : The first 7 coef's are are always hardcode and must
-   appear in the actual WAVE file.  They should be read in
-   in case a sound program added extras to the list. */
-
-static LONG gaiCoef1[] = { 256, 512, 0, 192, 240, 460,  392 };
-static LONG gaiCoef2[] = { 0, -256,  0,  64,   0,-208, -232};
-
-/*
- *
- * Lookup tables for IMA ADPCM format
- *
- */
-static int imaIndexAdjustTable[16] = {
-   -1, -1, -1, -1,  /* +0 - +3, decrease the step size */
-    2, 4, 6, 8,     /* +4 - +7, increase the step size */
-   -1, -1, -1, -1,  /* -0 - -3, decrease the step size */
-    2, 4, 6, 8,     /* -4 - -7, increase the step size */
-};
-
-static int imaStepSizeTable[89] = {
-   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
-};
-
 /****************************************************************************/
 /* IMA ADPCM Support Functions Section                                      */
 /****************************************************************************/
-
-/*
- *
- * MsAdpcmDecode - Decode a given sample and update state tables
- *
- */
-
-short ImaAdpcmDecode(deltaCode, state) 
-unsigned char deltaCode;
-ImaState_t *state;
-{
-    /* Get the current step size */
-   int step;
-   int difference;
-
-   step = imaStepSizeTable[state->index];
-   
-   /* Construct the difference by scaling the current step size */
-   /* This is approximately: difference = (deltaCode+.5)*step/4 */
-   difference = step>>3;
-   if ( deltaCode & 1 ) difference += step>>2;
-   if ( deltaCode & 2 ) difference += step>>1;
-   if ( deltaCode & 4 ) difference += step;
-
-   if ( deltaCode & 8 ) difference = -difference;
-
-   /* Build the new sample */
-   state->previousValue += difference;
-
-   if (state->previousValue > 32767) state->previousValue = 32767;
-   else if (state->previousValue < -32768) state->previousValue = -32768;
-
-   /* Update the step for the next sample */
-   state->index += imaIndexAdjustTable[deltaCode];
-   if (state->index < 0) state->index = 0;
-   else if (state->index > 88) state->index = 88;
-
-   return state->previousValue;
-
-}
-
-/*
- *
- * ImaAdpcmNextBlock - Grab and decode complete block of samples
- *
- */
-unsigned short  ImaAdpcmNextBlock(ft)
+unsigned short  ImaAdpcmReadBlock(ft)
 ft_t ft;    
 {
     wav_t	wav = (wav_t) ft->priv;
-    
-    /* Pull in the packet and check the header */
-    unsigned short bytesRead;
-    unsigned char *bytePtr;
+    int bytesRead;
+    int samplesThisBlock;
 
-    ImaState_t state[2];  /* One decompressor state for each channel */
-    int ch;
-    unsigned short remaining;
-    unsigned short samplesThisBlock;
-
-    int i;
-    unsigned char b;
-
+    /* Pull in the packet and check the header */
     bytesRead = fread(wav->packet,1,wav->blockAlign,ft->fp);
     if (bytesRead < wav->blockAlign) 
     { 
 	/* If it looks like a valid header is around then try and */
 	/* work with partial blocks.  Specs say it should be null */
-	/* padded but I guess this is better then trailing quite. */
+	/* padded but I guess this is better then trailing quiet. */
 	if (bytesRead >= (4 * ft->info.channels))
 	{
 	    samplesThisBlock = (wav->blockAlign - (3 * ft->info.channels));
@@ -216,123 +136,58 @@
     else
 	samplesThisBlock = wav->samplesPerBlock;
     
-    bytePtr = wav->packet;
+    wav->samplePtr = wav->samples;
+    
 
-    /* Read the four-byte header for each channel */
+    /* For a full block, the following should be true: */
+    /* wav->samplesPerBlock = blockAlign - 8byte header + 1 sample in header */
+    ImaBlockExpandI(ft->info.channels, wav->packet, wav->samples, samplesThisBlock);
+    return samplesThisBlock;
 
-    /* Reset the decompressor */
-    for(ch=0;ch < ft->info.channels; ch++) {
-       
-	/* Got this from xanim */
+}
 
-	state[ch].previousValue = ((int)bytePtr[1]<<8) +
-	    (int)bytePtr[0];
-	if (state[ch].previousValue & 0x8000)
-	    state[ch].previousValue -= 0x10000;
+static void ImaAdpcmWriteBlock(ft)
+ft_t ft;
+{
+    wav_t wav = (wav_t) ft->priv;
+    int chans, ch, ct;
+    short *p;
 
-	if (bytePtr[2] > 88)
-	{
-	    warn("IMA ADPCM Format Error (bad index value) in wav file");
-	    state[ch].index = 88;
-	}
-	else
-	    state[ch].index = bytePtr[2];
-	
-	if (bytePtr[3])
-	    warn("IMA ADPCM Format Error (synchronization error) in wav file");
-	
-	bytePtr+=4; /* Skip this header */
+    chans = ft->info.channels;
+    p = wav->samplePtr;
+    ct = p - wav->samples;
+    if (ct>=chans) { 
+	/* zero-fill samples if needed to complete block */
+	for (p = wav->samplePtr; p < wav->sampleTop; p++) *p=0;
+	/* compress the samples to wav->packet */
+	for (ch=0; ch<chans; ch++)
+	    ImaMashChannel(ch, chans, wav->samples, wav->samplesPerBlock, &wav->state[ch], wav->packet, 9);
 
-	wav->samplePtr[ch] = wav->samples[ch];
-	/* Decode one sample for the header */
-	*(wav->samplePtr[ch]++) = state[ch].previousValue;
+	/* write the compressed packet */
+	fwrite(wav->packet, wav->blockAlign, 1, ft->fp); /* FIXME: check return value */
+	/* update lengths and samplePtr */
+	wav->dataLength += wav->blockAlign;
+	wav->numSamples += ct/chans;
+	wav->samplePtr = wav->samples;
     }
+}
 
-    /* Decompress nybbles. Remainging is bytes in block minus header  */
-    /* Subtract the one sample taken from header */
-    remaining = samplesThisBlock-1;
-    
-    while (remaining) {
-	/* Always decode 8 samples */
-	remaining -= 8;
-	/* Decode 8 left samples */
-	for (i=0;i<4;i++) {
-	    b = *bytePtr++;
-	    *(wav->samplePtr[0]++) = ImaAdpcmDecode(b & 0x0f,&state[0]);
-	    *(wav->samplePtr[0]++) = ImaAdpcmDecode((b>>4) & 0x0f,&state[0]);
-	}
-	if (ft->info.channels < 2)
-	    continue; /* If mono, skip rest of loop */
-	/* Decode 8 right samples */
-	for (i=0;i<4;i++) {
-	    b = *bytePtr++;
-	    *(wav->samplePtr[1]++) = ImaAdpcmDecode(b & 0x0f,&state[1]);
-	    *(wav->samplePtr[1]++) = ImaAdpcmDecode((b>>4) & 0x0f,&state[1]);
-	}
-    }
-    /* For a full block, the following should be true: */
-    /* wav->samplesPerBlock = blockAlign - 8byte header + 1 sample in header */
-    return wav->samplesPerBlock;
-}     
-
 /****************************************************************************/
 /* MS ADPCM Support Functions Section                                       */
 /****************************************************************************/
-
 /*
  *
- * MsAdpcmDecode - Decode a given sample and update state tables
+ * MsAdpcmReadBlock - Grab and decode complete block of samples
  *
  */
-
-LONG MsAdpcmDecode(deltaCode, state) 
-LONG deltaCode;
-MsState_t *state;
-{
-    LONG predict;
-    LONG sample;
-    LONG idelta;
-
-    /** Compute next Adaptive Scale Factor (ASF) **/
-    idelta = state->index;
-    state->index = (gaiP4[deltaCode] * idelta) >> 8;
-    if (state->index < 16) state->index = 16;
-    if (deltaCode & 0x08) deltaCode = deltaCode - 0x10;
-    
-    /** Predict next sample **/
-    predict = ((state->sample1 * gaiCoef1[state->bpred]) + (state->sample2 * gaiCoef2[state->bpred])) >> 8;
-    /** reconstruct original PCM **/
-    sample = (deltaCode * idelta) + predict;
-    
-    if (sample > 32767) sample = 32767;
-    else if (sample < -32768) sample = -32768;
-    
-    state->sample2 = state->sample1;
-    state->sample1 = sample;
-    
-    return (sample);
-}
-    
-
-/*
- *
- * MsAdpcmNextBlock - Grab and decode complete block of samples
- *
- */
-unsigned short  MsAdpcmNextBlock(ft)
+unsigned short  MsAdpcmReadBlock(ft)
 ft_t ft;    
 {
     wav_t	wav = (wav_t) ft->priv;
-    
-    unsigned short bytesRead;
-    unsigned char *bytePtr;
+    int bytesRead;
+    int samplesThisBlock;
+    const char *errmsg;
 
-    MsState_t state[2];  /* One decompressor state for each channel */
-    unsigned short samplesThisBlock;
-    unsigned short remaining;
-
-    unsigned char b;
-
     /* Pull in the packet and check the header */
     bytesRead = fread(wav->packet,1,wav->blockAlign,ft->fp);
     if (bytesRead < wav->blockAlign) 
@@ -353,82 +208,11 @@
     else
 	samplesThisBlock = wav->samplesPerBlock;
     
-    bytePtr = wav->packet;
+    errmsg = AdpcmBlockExpandI(ft->info.channels, wav->packet, wav->samples, samplesThisBlock);
 
-    /* Read the four-byte header for each channel */
+    if (errmsg)
+	warn((char*)errmsg);
 
-    /* Reset the decompressor */
-    state[0].bpred = *bytePtr++;	/* Left */
-    if (ft->info.channels > 1)
-	state[1].bpred = *bytePtr++;	/* Right */
-    else
-	state[1].bpred = 0;
-
-    /* 7 should be variable from AVI/WAV header */
-    if (state[0].bpred >= 7)
-    {
-	warn("MSADPCM bpred %x and should be less than 7\n",state[0].bpred);
-	return(0);
-    }
-    if (state[1].bpred >= 7)
-    {
-	warn("MSADPCM bpred %x and should be less than 7\n",state[1].bpred);
-	return(0);
-    }
-	
-    state[0].index = *bytePtr++;  state[0].index |= (*bytePtr++)<<8;
-    if (state[0].index & 0x8000) state[0].index -= 0x10000;
-    if (ft->info.channels > 1)
-    {
-	state[1].index = *bytePtr++;  state[1].index |= (*bytePtr++)<<8;
-	if (state[1].index & 0x8000) state[1].index -= 0x10000;
-    }
-
-    state[0].sample1 = *bytePtr++;  state[0].sample1 |= (*bytePtr++)<<8;
-    if (state[0].sample1 & 0x8000) state[0].sample1 -= 0x10000;
-    if (ft->info.channels > 1)
-    {
-	state[1].sample1 = *bytePtr++;  state[1].sample1 |= (*bytePtr++)<<8;
-	if (state[1].sample1 & 0x8000) state[1].sample1 -= 0x10000;
-    }
-
-    state[0].sample2 = *bytePtr++;  state[0].sample2 |= (*bytePtr++)<<8;
-    if (state[0].sample2 & 0x8000) state[0].sample2 -= 0x10000;
-    if (ft->info.channels > 1)
-    {
-	state[1].sample2 = *bytePtr++;  state[1].sample2 |= (*bytePtr++)<<8;
-	if (state[1].sample2 & 0x8000) state[1].sample2 -= 0x10000;
-    }
-
-    wav->samplePtr[0] = wav->samples[0];
-    wav->samplePtr[1] = wav->samples[1];
-    
-    /* Decode two samples for the header */
-    *(wav->samplePtr[0]++) = state[0].sample2;
-    *(wav->samplePtr[0]++) = state[0].sample1;
-    if (ft->info.channels > 1)
-    {
-	*(wav->samplePtr[1]++) = state[1].sample2;
-	*(wav->samplePtr[1]++) = state[1].sample1;
-    }
-
-    /* Decompress nybbles.  Minus 2 included in header */
-    remaining = samplesThisBlock-2;
-
-    while (remaining) {
-	b = *bytePtr++;
-	*(wav->samplePtr[0]++) = MsAdpcmDecode((b>>4) & 0x0f, &state[0]);
-	remaining--;
-	if (ft->info.channels == 1)
-	{	    
-	    *(wav->samplePtr[0]++) = MsAdpcmDecode(b & 0x0f, &state[0]);
-	    remaining--;
-	}
-	else
-	{
-	    *(wav->samplePtr[1]++) = MsAdpcmDecode(b & 0x0f, &state[1]);
-	}
-    }
     return samplesThisBlock;
 }
 
@@ -458,8 +242,7 @@
     ULONG    wAvgBytesPerSec;	    /* estimate of bytes per second needed */
     unsigned short wBitsPerSample;  /* bits per sample */
     unsigned short wExtSize = 0;    /* extended field for ADPCM */
-    unsigned short wNumCoefs = 0;   /* Related to IMA ADPCM */
-	
+
     ULONG    data_length;	    /* length of sound data in bytes */
     ULONG    bytespersample;	    /* bytes per sample (per channel */
 
@@ -497,7 +280,7 @@
 	    len--;
 	}
     }
-
+    /* we only get here if we just read the magic "fmt " */
     if ( len < 16 )
 	fail("WAVE file fmt chunk is too short");
 
@@ -592,9 +375,14 @@
     wBitsPerSample =  rshort(ft);
     len -= 2;
 
+    wav->iCoefs = NULL;
+    wav->packet = NULL;
+    wav->samples = NULL;
+
     /* ADPCM formats have extended fmt chunk.  Check for those cases. */
-    if (wav->formatTag == WAVE_FORMAT_ADPCM)
+    switch (wav->formatTag)
     {
+    case WAVE_FORMAT_ADPCM:
 	if (wBitsPerSample != 4)
 	    fail("Can only handle 4-bit MS ADPCM in wav files");
 
@@ -601,19 +389,30 @@
 	wExtSize = rshort(ft);
 	wav->samplesPerBlock = rshort(ft);
 	wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;
-	wNumCoefs = rshort(ft);
+	wav->nCoefs = rshort(ft);
+	if (wav->nCoefs < 7 || wav->nCoefs > 0x100) {
+	    fail("ADPCM file nCoefs (%.4hx) makes no sense\n", wav->nCoefs);
+	}
 	wav->packet = (unsigned char *)malloc(wav->blockAlign);
 	len -= 6;
 	    
-	wav->samples[1] = wav->samples[0] = 0;
-	/* Use ft->info.channels after this becuase wChannels is now bad */
-	while (wChannels-- > 0)
-	    wav->samples[wChannels] = (short *)malloc(wav->samplesPerBlock*sizeof(short));
-	/* Here we are setting the bytespersample AFTER de-compression */
-	bytespersample = WORD;
-    }
-    else if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
-    {
+	wav->samples = (short *)malloc(wChannels*wav->samplesPerBlock*sizeof(short));
+
+	/* SJB: will need iCoefs later for adpcm.c */
+	wav->iCoefs = (short *)malloc(wav->nCoefs * 2 * sizeof(short));
+	{
+	    int i;
+	    for (i=0; len>=2 && i < 2*wav->nCoefs; i++) {
+		wav->iCoefs[i] = rshort(ft);
+		/* fprintf(stderr,"iCoefs[%2d] %4d\n",i,wav->iCoefs[i]); */
+		len -= 2;
+	    }
+	}
+
+	bytespersample = WORD;  /* AFTER de-compression */
+        break;
+
+    case WAVE_FORMAT_IMA_ADPCM:
 	if (wBitsPerSample != 4)
 	    fail("Can only handle 4-bit IMA ADPCM in wav files");
 
@@ -622,17 +421,14 @@
 	wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;
 	wav->packet = (unsigned char *)malloc(wav->blockAlign);
 	len -= 4;
-	    
-	wav->samples[1] = wav->samples[0] = 0;
-	/* Use ft->info.channels after this becuase wChannels is now bad */
-	while (wChannels-- > 0)
-	    wav->samples[wChannels] = (short *)malloc(wav->samplesPerBlock*sizeof(short));
-	/* Here we are setting the bytespersample AFTER de-compression */
-	bytespersample = WORD;
-    }
-    else
-    {
+
+	wav->samples = (short *)malloc(wChannels*wav->samplesPerBlock*sizeof(short));
+
+	bytespersample = WORD;  /* AFTER de-compression */
+	break;
+    default:
       bytespersample = (wBitsPerSample + 7)/8;
+
     }
 
     switch (bytespersample)
@@ -683,6 +479,9 @@
 	len--;
     }
 
+    /* for ADPCM formats, there's a useless(?) 'fact' chunk before
+     * the upcoming 'data' chunk */
+
     /* Now look for the wave data chunk */
     for (;;)
     {
@@ -700,20 +499,23 @@
     }
     
     data_length = len;
-    if (wav->formatTag == WAVE_FORMAT_ADPCM)
+
+    switch (wav->formatTag)
     {
+
+    case WAVE_FORMAT_ADPCM:
 	/* Compute easiest part of number of samples.  For every block, there
 	   are samplesPerBlock samples to read. */
 	wav->numSamples = (((data_length / wav->blockAlign) * wav->samplesPerBlock) * ft->info.channels);
-	/* Next, for any partial blocks, substract overhead from it and it
+	/* Next, for any partial blocks, subtract overhead from it and it
 	   will leave # of samples to read. */
 	wav->numSamples += ((data_length - ((data_length/wav->blockAlign)
 					    *wav->blockAlign))
 			    - (6 * ft->info.channels)) * ft->info.channels;
 	wav->blockSamplesRemaining = 0;	       /* Samples left in buffer */
-    }
-    else if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
-    {
+	break;
+
+    case WAVE_FORMAT_IMA_ADPCM:
 	/* Compute easiest part of number of samples.  For every block, there
 	   are samplesPerBlock samples to read. */
 	wav->numSamples = (((data_length / wav->blockAlign) * wav->samplesPerBlock) * ft->info.channels);
@@ -723,10 +525,14 @@
 					    *wav->blockAlign))
 			    - (3 * ft->info.channels)) * ft->info.channels;
 	wav->blockSamplesRemaining = 0;	       /* Samples left in buffer */
-    }
-    else
+	initImaTable();
+	break;
+
+    default:
 	wav->numSamples = data_length/ft->info.size;	/* total samples */
 
+    }
+
     report("Reading Wave file: %s format, %d channel%s, %d samp/sec",
 	   wav_format_str(wav->formatTag), ft->info.channels,
 	   wChannels == 1 ? "" : "s", wSamplesPerSecond);
@@ -733,11 +539,13 @@
     report("        %d byte/sec, %d block align, %d bits/samp, %u data bytes",
 	   wAvgBytesPerSec, wav->blockAlign, wBitsPerSample, data_length);
 
-    /* Can also report exteded fmt information */
+    /* Can also report extended fmt information */
     if (wav->formatTag == WAVE_FORMAT_ADPCM)
-	report("        %d Extsize, %d Samps/block, %d bytes/block %d Num Coefs\n",wExtSize,wav->samplesPerBlock,wav->bytesPerBlock,wNumCoefs);
+	report("        %d Extsize, %d Samps/block, %d bytes/block %d Num Coefs\n",
+		wExtSize,wav->samplesPerBlock,wav->bytesPerBlock,wav->nCoefs);
     else if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
-	report("        %d Extsize, %d Samps/block, %d bytes/block\n",wExtSize,wav->samplesPerBlock,wav->bytesPerBlock);
+	report("        %d Extsize, %d Samps/block, %d bytes/block\n",
+		wExtSize,wav->samplesPerBlock,wav->bytesPerBlock);
 }
 
 /*
@@ -758,16 +566,17 @@
 
 	/* If file is in ADPCM style then read in multiple blocks else */
 	/* read as much as possible and return quickly. */
-	if (ft->info.style == ADPCM)
+	switch (ft->info.style)
 	{
+	case ADPCM:
 	    done = 0;
 	    while (done < len) { /* Still want data? */
 		/* See if need to read more from disk */
 		if (wav->blockSamplesRemaining == 0) { 
 		    if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
-			wav->blockSamplesRemaining = ImaAdpcmNextBlock(ft);
+			wav->blockSamplesRemaining = ImaAdpcmReadBlock(ft);
 		    else
-			wav->blockSamplesRemaining = MsAdpcmNextBlock(ft);
+			wav->blockSamplesRemaining = MsAdpcmReadBlock(ft);
 		    if (wav->blockSamplesRemaining == 0)
 		    {
 			/* Don't try to read any more samples */
@@ -774,43 +583,40 @@
 			wav->numSamples = 0;
 			return done;
 		    }
-		    wav->samplePtr[0] = wav->samples[0];
-		    wav->samplePtr[1] = wav->samples[1];
+		    wav->blockSamplesRemaining *= ft->info.channels;
+		    wav->samplePtr = wav->samples;
 		}
 
-		switch(ft->info.channels) { /* Copy data into buf */
-		case 1: /* Mono: Just copy left channel data */
-		    while ((wav->blockSamplesRemaining > 0) && (done < len))
+		/* Copy interleaved data into buf, converting short to LONG */
+		{
+		    short *p, *top;
+		    int ct;
+		    ct = len-done;
+		    if (ct > wav->blockSamplesRemaining)
+			ct = wav->blockSamplesRemaining;
+
+		    done += ct;
+		    wav->blockSamplesRemaining -= ct;
+		    p = wav->samplePtr;
+		    top = p+ct;
+		    /* Output is already signed */
+		    while (p<top)
 		    {
-			/* Output is already signed */
-			*buf++ = LEFT(*(wav->samplePtr[0]++), 16);
-			done++;
-			wav->blockSamplesRemaining--;
+			*buf++ = LEFT((*p++), 16);
 		    }
-		    break;
-		case 2: /* Stereo: Interleave samples */
-		    while ((wav->blockSamplesRemaining > 0) && (done < len))
-		    {
-			/* Output is already signed */
-			*buf++ = LEFT(*(wav->samplePtr[0]++),16); /* Left */
-			*buf++ = LEFT(*(wav->samplePtr[1]++),16); /* Right */
-			done += 2;
-			wav->blockSamplesRemaining--;
-		    }
-		    break;
-		default:
-		    fail ("Can only handle stereo or mono files");
+		    wav->samplePtr = p;
 		}
 	    }
-	}
-	else /* else not ADPCM style */
-	{
+	    break;
+
+	default: /* not ADPCM style */
 	    done = rawread(ft, buf, len);
 	    /* If software thinks there are more samples but I/O */
-	    /* says otherwise, let the user no about this.       */
+	    /* says otherwise, let the user know about this.     */
 	    if (done == 0 && wav->numSamples != 0)
 		warn("Premature EOF on .wav input file");
 	}
+
 	wav->numSamples -= done;
 	return done;
 }
@@ -825,8 +631,8 @@
     wav_t	wav = (wav_t) ft->priv;
 
     if (wav->packet) free(wav->packet);
-    if (wav->samples[0]) free(wav->samples[0]);
-    if (wav->samples[1]) free(wav->samples[1]);
+    if (wav->samples) free(wav->samples);
+    if (wav->iCoefs) free(wav->iCoefs);
 
     /* Needed for rawread() */
     rawstopread(ft);
@@ -843,22 +649,43 @@
 	if (!*endptr) ft->swap = ft->swap ? 0 : 1;
 
 	wav->numSamples = 0;
-	wav->second_header = 0;
-	if (! ft->seekable)
-		warn("Length in output .wav header will wrong since can't seek to fix it");
-	wavwritehdr(ft);
+	wav->dataLength = 0;
+	if (!ft->seekable)
+		warn("Length in output .wav header will be wrong since can't seek to fix it");
+	wavwritehdr(ft, 0);  /* also calculates various wav->* info */
+	wav->packet = NULL;
+	wav->samples = NULL;
+	wav->iCoefs = NULL;
+	if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM) {
+	    int ch, sbsize;
+	    /* #channels already range-checked for overflow in wavwritehdr() */
+	    for (ch=0; ch<ft->info.channels; ch++)
+	    	wav->state[ch] = 0;
+	    sbsize = ft->info.channels * wav->samplesPerBlock;
+	    wav->packet = (unsigned char *)malloc(wav->blockAlign);
+	    wav->samples = (short *)malloc(sbsize*sizeof(short));
+	    wav->sampleTop = wav->samples + sbsize;
+	    wav->samplePtr = wav->samples;
+	    initImaTable();
+	}
 }
 
-void wavwritehdr(ft) 
+#define WHDRSIZ1 8 /* "RIFF",(long)len,"WAVE" */
+#define FMTSIZ1 24 /* "fmt ",(long)len,... fmt chunk (without any Ext data) */
+#define DATASIZ1 8 /* "data",(long)len        */
+void wavwritehdr(ft, second_header) 
 ft_t ft;
+int second_header;
 {
 	wav_t	wav = (wav_t) ft->priv;
+	LONG fmtsize = FMTSIZ1;
+	LONG factsize = 0; /* "fact",(long)len,??? */
 
         /* wave file characteristics */
         unsigned short wFormatTag = 0;          /* data format */
         unsigned short wChannels;               /* number of channels */
         ULONG  wSamplesPerSecond;       	/* samples per second per channel */
-        ULONG  wAvgBytesPerSec;        		 /* estimate of bytes per second needed */
+        ULONG  wAvgBytesPerSec;        		/* estimate of bytes per second needed */
         unsigned short wBlockAlign;             /* byte alignment of a basic sample block */
         unsigned short wBitsPerSample;          /* bits per sample */
         ULONG  data_length;             	/* length of sound data in bytes */
@@ -865,7 +692,8 @@
 	ULONG  bytespersample; 			/* bytes per sample (per channel) */
 
 	/* Needed for rawwrite() */
-	rawstartwrite(ft);
+	if (ft->info.style != ADPCM)
+		rawstartwrite(ft);
 
 	switch (ft->info.size)
 	{
@@ -874,7 +702,7 @@
 			if (ft->info.style != UNSIGNED &&
 			    ft->info.style != ULAW &&
 			    ft->info.style != ALAW &&
-			    !wav->second_header)
+			    !second_header)
 			{
 				warn("Only support unsigned, ulaw, or alaw with 8-bit data.  Forcing to unsigned");
 				ft->info.style = UNSIGNED;
@@ -885,7 +713,7 @@
 			if ((ft->info.style == UNSIGNED ||
 			     ft->info.style == ULAW ||
 			     ft->info.style == ALAW) &&
-			    !wav->second_header)
+			    !second_header)
 			{
 				warn("Do not support Unsigned, ulaw, or alaw with 16 bit data.  Forcing to Signed");
 				ft->info.style = SIGN2;
@@ -914,28 +742,34 @@
 			wFormatTag = WAVE_FORMAT_MULAW;
 			break;
 		case ADPCM:
-			wFormatTag = WAVE_FORMAT_PCM;
-		        warn("Can not support writing ADPCM style. Overriding to Signed Words\n");
-			ft->info.style = SIGN2;
-			wBitsPerSample = 16;
-			/* wFormatTag = WAVE_FORMAT_IMA_ADPCM;
-			   wBitsPerSample = 4;
-			if (wBitsPerSample != 4 && !wav->second_header)
-			break; */
+		        warn("Experimental support writing IMA_ADPCM style.\n");
+			wFormatTag = WAVE_FORMAT_IMA_ADPCM;
+			wBitsPerSample = 4;
+			fmtsize += 4;  /* plus ExtData */
+			factsize = 12; /* fact chunk   */
+			break;
 	}
+	wav->formatTag = wFormatTag;
 	
-	
 	wSamplesPerSecond = ft->info.rate;
-	bytespersample = (wBitsPerSample + 7)/8;
-	wAvgBytesPerSec = ft->info.rate * ft->info.channels * bytespersample;
 	wChannels = ft->info.channels;
-	wBlockAlign = ft->info.channels * bytespersample;
-	if (!wav->second_header)	/* use max length value first time */
-		data_length = 0x7fffffffL - (8+16+12);
+	if (wFormatTag == WAVE_FORMAT_IMA_ADPCM) {
+	    if (wChannels>16)
+	    	fail("Channels(%d) must be <= 16\n",wChannels);
+	    bytespersample = 2;
+	    wBlockAlign = wChannels * 64; /* reasonable default */
+	} else {
+	    bytespersample = (wBitsPerSample + 7)/8;
+	    wBlockAlign = wChannels * bytespersample;
+	}
+	wav->blockAlign = wBlockAlign;
+	wAvgBytesPerSec = ft->info.rate * wChannels * bytespersample;
+	if (!second_header)	/* use max length value first time */
+		data_length = 0x7fffffffL - (WHDRSIZ1+fmtsize+factsize+DATASIZ1);
 	else	/* fixup with real length */
 	{
 	    if (ft->info.style == ADPCM)
-		data_length = wav->numSamples / 2;
+		data_length = wav->dataLength; /* FIXME: not really */
 	    else
 		data_length = bytespersample * wav->numSamples;
 	}
@@ -942,21 +776,36 @@
 
 	/* figured out header info, so write it */
 	fputs("RIFF", ft->fp);
-	wlong(ft, data_length + 8+16+12);	/* Waveform chunk size: FIXUP(4) */
+	wlong(ft, data_length + WHDRSIZ1+fmtsize+factsize+DATASIZ1);/* Waveform chunk size: FIXUP(4) */
 	fputs("WAVE", ft->fp);
 	fputs("fmt ", ft->fp);
-	wlong(ft, (LONG)16);		/* fmt chunk size */
+	wlong(ft, fmtsize-8);	/* fmt chunk size */
 	wshort(ft, wFormatTag);
 	wshort(ft, wChannels);
 	wlong(ft, wSamplesPerSecond);
 	wlong(ft, wAvgBytesPerSec);
 	wshort(ft, wBlockAlign);
-	wshort(ft, wBitsPerSample);
-	
+	wshort(ft, wBitsPerSample); /* end of info common to all fmts */
+	switch (wFormatTag)
+	{
+	int nsamp;
+	case WAVE_FORMAT_IMA_ADPCM:
+	    wshort(ft, 2);		/* Ext fmt data length */
+	    wav->samplesPerBlock = ((wBlockAlign - 4*wChannels)/(4*wChannels))*8 + 1;
+	    wshort(ft, wav->samplesPerBlock);
+	    fputs("fact", ft->fp);
+	    wlong(ft, factsize-8);	/* fact chunk size */
+	    /* use max nsamps value first time */
+	    nsamp = (second_header)? wav->numSamples : 0x7fffffffL;
+	    wlong(ft, nsamp);
+	    break;
+	default:
+	}
+
 	fputs("data", ft->fp);
 	wlong(ft, data_length);		/* data chunk size: FIXUP(40) */
 
-	if (!wav->second_header) {
+	if (!second_header) {
 		report("Writing Wave file: %s format, %d channel%s, %d samp/sec",
 	        	wav_format_str(wFormatTag), wChannels,
 	        	wChannels == 1 ? "" : "s", wSamplesPerSecond);
@@ -972,16 +821,44 @@
 {
 	wav_t	wav = (wav_t) ft->priv;
 
-	wav->numSamples += len;
-	rawwrite(ft, buf, len);
+	switch (wav->formatTag)
+	{
+	case WAVE_FORMAT_IMA_ADPCM:
+	    while (len>0) {
+		short *p = wav->samplePtr;
+		short *top = wav->sampleTop;
+
+		if (top>p+len) top = p+len;
+		len -= top-p; /* update residual len */
+		while (p < top)
+		   *p++ = ((*buf++) + 0x8000) >> 16;
+
+		wav->samplePtr = p;
+		if (p == wav->sampleTop)
+		    ImaAdpcmWriteBlock(ft);
+
+	    }
+	    break;
+	default:
+	    wav->numSamples += len;
+	    rawwrite(ft, buf, len);
+	}
 }
 
-void
-wavstopwrite(ft) 
+void wavstopwrite(ft) 
 ft_t ft;
 {
+	wav_t	wav = (wav_t) ft->priv;
 	/* Call this to flush out any remaining data. */
-	rawstopwrite(ft);
+	if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM) {
+	    ImaAdpcmWriteBlock(ft);
+	} else {
+	    rawstopwrite(ft);
+	}
+    	
+	if (wav->packet) free(wav->packet);
+ 	if (wav->samples) free(wav->samples);
+ 	if (wav->iCoefs) free(wav->iCoefs);
 
 	/* All samples are already written out. */
 	/* If file header needs fixing up, for example it needs the */
@@ -990,8 +867,7 @@
 		return;
 	if (fseek(ft->fp, 0L, SEEK_SET) != 0)
 		fail("Sorry, can't rewind output file to rewrite .wav header.");
-	((wav_t) ft->priv)->second_header = 1;
-	wavwritehdr(ft);
+	wavwritehdr(ft, 1);
 }
 
 /*
--- a/src/wav.h
+++ b/src/wav.h
@@ -26,17 +26,5 @@
 #define WAVE_FORMAT_G726_ADPCM          (0x0064)
 #define WAVE_FORMAT_G722_ADPCM          (0x0065)
 
-typedef struct MsState {
-    LONG  index;	/* Index into step size table */
-    ULONG bpred;	/* Most recent sample value */
-    LONG  sample1;
-    LONG  sample2;
-} MsState_t;
-
-typedef struct ImaState {
-   int index;    	/* Index into step size table */
-   int previousValue; 	/* Most recent sample value */
-} ImaState_t;
-
-
 #endif /* WAV_H_INCLUDED */
+