shithub: sox

Download patch

ref: 613f50d018d73308428dda8c610066a726e1a95e
parent: da6bd76bcbdbad47ce45df93d3e000b91bc85673
author: cbagwell <cbagwell>
date: Mon Nov 22 13:11:52 EST 1999

Adding MS and IMA ADPCM write support!

--- a/Changelog
+++ b/Changelog
@@ -6,8 +6,9 @@
 
 sox-12.17
 ---------
-  o Sox can now write IMA_ADPCM compressed .wav,
-	  MSADPCM writing should be available soon.
+  o Sox can now write IMA_ADPCM and ADPCM compressed .wav,
+    this is new, and relatively untested. See -i and -a format
+		flags in manpage.
   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.
--- a/TODO
+++ b/TODO
@@ -1,12 +1,6 @@
 People are encouraged to pick some of these and implement it.  Send
 all patches to cbagwell@sprynet.com.
 
-  o Add support for writing ADPCM output in .wav files.  It really only
-    feasible to add one version of ADPCM to output.  I'm leaning towards
-    IMA since it could support streaming and be used in other formats.
-    But of course, MS is pushing very hard to make their version the
-    standard, weither inferer or not.
-
   o I've add the most common and public standard FormatTags to the
     .wav file detection.  A lot of them should be implemented since
     you can grab the source code from the .au handler and software
--- a/sox.1
+++ b/sox.1
@@ -9,7 +9,7 @@
 .if t .sp .5v
 .if n .sp
 ..
-.TH SoX 1 "November 8, 1999"
+.TH SoX 1 "November 22, 1999"
 .SH NAME
 sox \- Sound eXchange : universal sound sample translator
 .SH SYNOPSIS
@@ -39,7 +39,7 @@
 \fIFormat options:\fB
 [ \fB-t \fIfiletype\fB ]
 [ -r \fIrate\fB ]
-[ -s/-u/-U/-A/-a/-g ]
+[ -s/-u/-U/-A/-a/-i/-g ]
 [ -b/-w/-l/-f/-d/-D ]
 [ -c \fIchannels\fB ]
 [ -x ]
@@ -146,15 +146,18 @@
 sample rate changing effect is not specified then a default one will be
 used with its default parameters.
 .TP 10
-\fB-s/-u/-U/-A/-a/-g\fR
+\fB-s/-u/-U/-A/-a/-i/-g\fR
 The sample data is signed linear (2's complement),
 unsigned linear, U-law (logarithmic), A-law (logarithmic),
-ADPCM, or GSM.
+ADPCM, IMA_ADPCM, or GSM.
 U-law and A-law are the U.S. and international
 standards for logarithmic telephone sound compression.
 ADPCM is form of sound compression that has a good
 compromise between good sound quality and fast encoding/decoding
 time.
+IMA_ADPCM is also a form of adpcm compression, slightly simpler
+and slightly lower fidelity than Microsoft's flavor of ADPCM.
+IMA_ADPCM is also called DVI_ADPCM.
 GSM is a standard used for telephone sound compression in
 European countries and its gaining popularity because of its
 quality.
@@ -440,9 +443,11 @@
 will written appropriately.  Note that it is possible to
 write data of a type that cannot be specified by
 the \fB.wav\fR header, and you will be warned that
-you a writing a bad file !
+you are writing a bad file !
 Sox currently can read PCM, ULAW, ALAW, MS ADPCM, and IMA (or DVI) ADPCM.
-It can output all of these formats except the ADPCM styles.
+It can write all of these formats including
+.B (NEW!)
+the ADPCM styles.
 .TP 10
 .B .wve
 Psion 8-bit alaw
--- a/src/adpcm.c
+++ b/src/adpcm.c
@@ -23,12 +23,13 @@
  */
 
 /*
- * November 20, 1999
+ * November 22, 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
+ *  Remark: code still turbulent, encoding very new.
+ *
  */
 
 #include <sys/types.h>
@@ -59,7 +60,7 @@
    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] = {
+short iCoef[7][2] = {
 			{ 256,   0},
 			{ 512,-256},
 			{   0,   0},
@@ -80,27 +81,28 @@
 LONG c, sample1, sample2;
 MsState_t *state;
 {
-	LONG predict;
+	LONG vlin;
 	LONG sample;
-	LONG step, step1;
+	LONG step;
 
 	/** Compute next step value **/
 	step = state->step;
-	step1 = (stepAdjustTable[c] * step) >> 8;
-	state->step = (step1 < 16)? 16:step1;
+	{
+		LONG nstep;
+		nstep = (stepAdjustTable[c] * step) >> 8;
+		state->step = (nstep < 16)? 16:nstep;
+	}
 
 	/** make linear prediction for next sample **/
-	predict =
+	vlin =
 			((sample1 * state->iCoef[0]) +
 			 (sample2 * state->iCoef[1])) >> 8;
 	/** then add the code*step adjustment **/
 	c -= (c & 0x08) << 1;
-	sample = (c * step) + predict;
+	sample = (c * step) + vlin;
 
-	if (sample > 32767)
-		sample = 32767;
-	else if (sample < -32768)
-		sample = -32768;
+	if (sample > 0x7fff) sample = 0x7fff;
+	else if (sample < -0x8000) sample = -0x8000;
 
 	return (sample);
 }
@@ -162,4 +164,228 @@
 		}
 	}
 	return errmsg;
+}
+
+static int AdpcmMashS(
+	int ch,              /* channel number to encode, REQUIRE 0 <= ch < chans  */
+	int chans,           /* total channels */
+	SAMPL v[2],          /* values to use as starting 2 */
+	const short iCoef[2],/* lin predictor coeffs */
+	const SAMPL *ibuff,  /* ibuff[] is interleaved input samples */
+	int n,               /* samples to encode PER channel */
+	int *iostep,         /* input/output step, REQUIRE 16 <= *st <= 0x7fff */
+	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 ox = 0;      /*  */
+	int i, d, v0, v1, step;
+	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;
+	v0 = v[0];
+	v1 = v[1];
+	d = *ip - v1; ip += chans; /* 1st input sample for this channel */
+	d2 = d*d;  /* d2 will be sum of squares of errors, given input v0 and *st */
+	d = *ip - v0; ip += chans; /* 2nd input sample for this channel */
+	d2 += d*d;
+
+	step = *iostep;
+
+	op = obuff;            /* output pointer (or NULL) */
+	if (op) {              /* NULL means don't output, just compute the rms error */
+		op += chans;         /* skip bpred indices */
+		op += 2*ch;          /* channel's stepsize */
+		op[0] = step; op[1] = step>>8;
+		op += 2*chans;       /* skip to v0 */
+		op[0] = v0; op[1] = v0>>8;
+		op += 2*chans;       /* skip to v1 */
+		op[0] = v1; op[1] = v1>>8;
+		op = obuff+7*chans;  /* point to base of output nibbles */
+		ox = 4*ch;
+	}
+	for (i = 0; ip < itop; ip+=chans) {
+		int vlin,d,dp,c;
+
+	  /* make linear prediction for next sample */
+		vlin = (v0 * iCoef[0] + v1 * iCoef[1]) >> 8;
+		d = *ip - vlin;  /* difference between linear prediction and current sample */
+		dp = d + (step<<3) + (step>>1);
+		c = 0;
+		if (dp>0) {
+			c = dp/step;
+			if (c>15) c = 15;
+		}
+		c -= 8;
+		dp = c * step;   /* quantized estimate of samp - vlin */
+		c &= 0x0f;       /* mask to 4 bits */
+
+		v1 = v0; /* shift history */
+		v0 = vlin + dp;
+		if (v0<-0x8000) v0 = -0x8000;
+		else if (v0>0x7fff) v0 = 0x7fff;
+
+		d = *ip - v0;
+		d2 += d*d; /* update square-error */
+
+		if (op) {   /* if we want output, put it in proper place */
+			/* FIXME: does c<<0 work properly? */
+			op[ox>>3] |= (ox&4)? c:(c<<4);
+			ox += 4*chans;
+			/* if (sho) fprintf(stderr,"%.1x",c); */
+
+		}
+
+		/* Update the step for the next sample */
+		step = (stepAdjustTable[c] * step) >> 8;
+		if (step < 16) step = 16;
+
+	}
+	/* if (sho && op) fprintf(stderr,"\n");*/
+	d2 /= n; /* be sure it's non-negative */
+#ifdef DEBUG
+	if (sho) {
+		fprintf(stderr, "ch%d: st %d->%d, d %.1f\n", ch, *iostep, step, sqrt(d2));
+		fflush(stderr);
+	}
+#endif
+	*iostep = step;
+	return (int) sqrt(d2);
+}
+
+#if 0
+
+static long AvgDelta(int ch, int chans, const SAMPL *ibuff, int n)
+{
+	const SAMPL *ip, *itop;
+	long v0;
+	long d1;
+	
+	ip = ibuff + ch;
+	itop = ip + n*chans;
+	d1 = 0;
+	v0 = *ip;
+	ip += chans;
+	for ( ; ip < itop; ip+=chans) {
+		long v1;
+
+		v1 = *ip;
+		d1 = abs(v1-v0);
+		v0 = v1;
+	}
+	return (d1/(n-1));
+}
+
+static long ReAvgDelta(int ch, int chans, const SAMPL *ibuff, int n, int step)
+{
+	const SAMPL *ip, *itop;
+	long v0;
+	long d1;
+	
+	ip = ibuff + ch;
+	itop = ip + n*chans;
+	d1 = 0;
+	v0 = *ip;
+	ip += chans;
+	for ( ; ip < itop; ip+=chans) {
+		long v1, c;
+
+		v1 = *ip;
+		c = abs(v1-v0);
+		if (step && c>2*step) c=2*step;
+		d1 += c;
+		v0 = v1;
+	}
+	return (d1/(n-1));
+}
+
+#endif
+
+#ifdef __GNUC__
+inline
+#endif
+static void AdpcmMashChannel(
+	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 */
+	int *st,            /* input/output steps, 16<=st[i] */
+	u_char *obuff,      /* output buffer[blockAlign] */
+	int opt             /* non-zero allows some cpu-intensive code to improve output */
+)
+{
+	SAMPL v[2];
+	int n0,s0,s1,ss,smin;
+	int d,dmin,k,kmin;
+	
+	n0 = n/2; if (n0>32) n0=32;
+#if 0
+	s0=ReAvgDelta(ch, chans, ip, n, 0);
+	s1=ReAvgDelta(ch, chans, ip, n, s0);
+	fprintf(stderr, "ReAvg%d: %d->%d (%d)\n", ch, s0,s1,*st);
+	fflush(stderr);
+#endif
+	if (*st<16) *st = 16;
+	v[1] = ip[ch];
+	v[0] = ip[ch+chans];
+
+	dmin = 0; kmin = 0; smin = 0;
+	/* for each of 7 standard coeff sets, we try compression 
+	 * beginning with last step-value, and with slightly
+	 * forward-adjusted step-value, taking best of the 14
+	 */
+	for (k=0; k<7; k++) {
+		int d0,d1;
+		ss = s0 = *st;
+		d0=AdpcmMashS(ch, chans, v, iCoef[k], ip, n, &ss, NULL, 0); /* with step s0 */
+
+		s1 = s0;
+		AdpcmMashS(ch, chans, v, iCoef[k], ip, n0, &s1, NULL, 0);
+		/* fprintf(stderr," s32 %d\n",s1); */
+		ss = s1 = (3*s0+s1)/4;
+		d1=AdpcmMashS(ch, chans, v, iCoef[k], ip, n, &ss, NULL, 0); /* with step s1 */
+		if (!k || d0<dmin || d1<dmin) {
+			kmin = k;
+			if (d0<=d1) {
+				dmin = d0;
+				smin = s0;
+			}else{
+				dmin = d1;
+				smin = s1;
+			}
+		}
+	}
+	*st = smin;
+#ifdef DEBUG
+	fprintf(stderr,"kmin %d, smin %5d, ",kmin,smin);
+	d=AdpcmMashS(ch, chans, v, iCoef[kmin], ip, n, st, obuff, 1);
+#else
+	d=AdpcmMashS(ch, chans, v, iCoef[kmin], ip, n, st, obuff, 0);
+#endif
+	obuff[ch] = kmin;
+}
+
+void AdpcmMashI(
+	int chans,          /* total channels */
+	const SAMPL *ip,    /* ip[n*chans] is interleaved input samples */
+	int n,              /* samples to encode PER channel */
+	int *st,            /* input/output steps, 16<=st[i] */
+	u_char *obuff,      /* output buffer[blockAlign] */
+	int blockAlign,     /* >= 7*chans + n/2          */
+	int opt             /* non-zero allows some cpu-intensive code to improve output */
+)
+{
+	int ch;
+	u_char *p;
+
+	/*fprintf(stderr,"AdpcmMashI(chans %d, ip %p, n %d, st %p, obuff %p, bA %d)\n",
+								 chans, ip, n, st, obuff, blockAlign);*/
+
+	for (p=obuff+7*chans; p<obuff+blockAlign; p++) *p=0;
+
+	for (ch=0; ch<chans; ch++)
+		AdpcmMashChannel(ch, chans, ip, n, st+ch, obuff, opt);
 }
--- a/src/adpcm.h
+++ b/src/adpcm.h
@@ -11,6 +11,9 @@
 #define ULONG u_long
 #endif
 
+/* default coef sets */
+extern short iCoef[7][2];
+
 /* AdpcmBlockExpandI() outputs interleaved samples into one output buffer */
 extern const char *AdpcmBlockExpandI(
 	int chans,          /* total channels             */
@@ -17,5 +20,15 @@
 	const u_char *ibuff,/* input buffer[blockAlign]   */
 	SAMPL *obuff,       /* output samples, n*chans    */
 	int n               /* samples to decode PER channel, REQUIRE n % 8 == 1  */
+);
+
+extern void AdpcmMashI(
+	int chans,          /* total channels */
+	const SAMPL *ip,    /* ip[n*chans] is interleaved input samples */
+	int n,              /* samples to encode PER channel, REQUIRE */
+	int *st,            /* input/output steps, 16<=st[i] */
+	u_char *obuff,      /* output buffer[blockAlign] */
+	int blockAlign,     /* >= 7*chans + n/2          */
+	int opt             /* non-zero allows some cpu-intensive code to improve output */
 );
 
--- a/src/mask.c
+++ b/src/mask.c
@@ -12,6 +12,7 @@
  * Sound Tools masking noise effect file.
  */
 
+#include <stdlib.h>
 #include <math.h>
 #include "st.h"
 #include "libst.h"
--- a/src/misc.c
+++ b/src/misc.c
@@ -36,7 +36,8 @@
 	"u-law",
 	"a-law",
 	"adpcm",
-	"gsm"
+	"ima_adpcm",
+	"gsm",
 };
 
 char readerr[] = "Premature EOF while reading sample file.";
--- a/src/sox.c
+++ b/src/sox.c
@@ -206,9 +206,9 @@
 }
 
 #ifdef HAVE_GETOPT_H
-char *getoptstr = "+r:v:t:c:phsuUAagbwlfdDxV";
+char *getoptstr = "+r:v:t:c:phsuUAaigbwlfdDxV";
 #else
-char *getoptstr = "r:v:t:c:phsuUAagbwlfdDxV";
+char *getoptstr = "r:v:t:c:phsuUAaigbwlfdDxV";
 #endif
 
 void doopts(argc, argv)
@@ -314,6 +314,10 @@
 			if (! ft) usage("-a");
 			ft->info.style = ADPCM;
 			break;
+		case 'i':
+			if (! ft) usage("-i");
+			ft->info.style = IMA_ADPCM;
+			break;
 		case 'g':
 			if (! ft) usage("-g");
 			ft->info.style = GSM;
@@ -894,7 +898,7 @@
 		fprintf(stderr, "Failed at: %s\n", opt);
 	else {
 	    fprintf(stderr,"gopts: -e -h -p -v volume -V\n\n");
-	    fprintf(stderr,"fopts: -r rate -c channels -s/-u/-U/-A/-a/-g -b/-w/-l/-f/-d/-D -x\n\n");
+	    fprintf(stderr,"fopts: -r rate -c channels -s/-u/-U/-A/-a/-i/-g -b/-w/-l/-f/-d/-D -x\n\n");
 	    fprintf(stderr, "effect: ");
 	    for (i = 1; effects[i].name != NULL; i++) {
 		fprintf(stderr, "%s ", effects[i].name);
--- a/src/st.h
+++ b/src/st.h
@@ -152,7 +152,8 @@
 #define	ULAW		3	/* U-law signed logs: US telephony, SPARC */
 #define ALAW		4	/* A-law signed logs: non-US telephony */
 #define ADPCM		5	/* Compressed PCM */
-#define GSM		6	/* GSM 6.10 33-byte frame lossy compression */
+#define IMA_ADPCM		6	/* Compressed PCM */
+#define GSM		7	/* GSM 6.10 33-byte frame lossy compression */
 
 /* FIXME: This shouldn't be defined inside library.  Only needed
  * by sox.c itself.  Delete from raw.c and misc.c.
--- a/src/wav.c
+++ b/src/wav.c
@@ -8,12 +8,12 @@
  *
  * 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.
+ * November  22, 1999 - Stan Brooks (stabro@megsinet.com)
+ *   Mods for faster adpcm decoding and addition of IMA_ADPCM
+ *   and 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 written for adpcm include the 'fact' subchunk.
  *
  * September 11, 1998 - Chris Bagwell (cbagwell@sprynet.com)
  *   Fixed length bug for IMA and MS ADPCM files.
@@ -26,23 +26,11 @@
  *
  * March 15, 1998 - Chris Bagwell (cbagwell@sprynet.com)
  *   Added support for Microsoft's ADPCM and IMA (or better known as
- *   DVI) ADPCM format for wav files.  Info on these formats
- *   was taken from the xanim project, written by
- *   Mark Podlipec (podlipec@ici.net).  For those pieces of code,
- *   the following copyrights notice applies:
+ *   DVI) ADPCM format for wav files.  Thanks goes to Mark Podlipec's
+ *   XAnim code.  It gave some real life understanding of how the ADPCM
+ *   format is processed.  Actual code was implemented based off of
+ *   various sources from the net.
  *
- *    XAnim Copyright (C) 1990-1997 by Mark Podlipec.
- *    All rights reserved.
- * 
- *    This software may be freely copied, modified and redistributed without
- *    fee for non-commerical purposes provided that this copyright notice is
- *    preserved intact on all copies and modified copies.
- * 
- *    There is no warranty or other guarantee of fitness of this software.
- *    It is provided solely "as is". The author(s) disclaim(s) all
- *    responsibility and liability with respect to this software's usage
- *    or its effect upon hardware or computer systems.
- *
  * NOTE: Previous maintainers weren't very good at providing contact
  * information.
  *
@@ -97,6 +85,7 @@
     short	  *samplePtr;       /* Pointer to current samples */
     short	  *sampleTop;       /* End of samples-buffer      */
     unsigned short blockSamplesRemaining;/* Samples remaining in each channel */    
+    /* state holds step-size info for ADPCM or IMA_ADPCM writes */
     int 	   state[16];       /* last, because maybe longer */
 } *wav_t;
 
@@ -177,10 +166,10 @@
 /****************************************************************************/
 /*
  *
- * MsAdpcmReadBlock - Grab and decode complete block of samples
+ * AdpcmReadBlock - Grab and decode complete block of samples
  *
  */
-unsigned short  MsAdpcmReadBlock(ft)
+unsigned short  AdpcmReadBlock(ft)
 ft_t ft;    
 {
     wav_t	wav = (wav_t) ft->priv;
@@ -216,6 +205,32 @@
     return samplesThisBlock;
 }
 
+static void AdpcmWriteBlock(ft)
+ft_t ft;
+{
+    wav_t wav = (wav_t) ft->priv;
+    int chans, ct;
+    short *p;
+
+    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 */
+
+	AdpcmMashI(chans, wav->samples, wav->samplesPerBlock, wav->state, wav->packet, wav->blockAlign,9);
+
+	/* 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;
+    }
+}
+
 /****************************************************************************/
 /* General Sox WAV file code                                                */
 /****************************************************************************/
@@ -388,7 +403,8 @@
 
 	wExtSize = rshort(ft);
 	wav->samplesPerBlock = rshort(ft);
-	wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;
+	wav->bytesPerBlock = ((wav->samplesPerBlock-2)*ft->info.channels + 1)/2
+		             + 7*ft->info.channels;
 	wav->nCoefs = rshort(ft);
 	if (wav->nCoefs < 7 || wav->nCoefs > 0x100) {
 	    fail("ADPCM file nCoefs (%.4hx) makes no sense\n", wav->nCoefs);
@@ -395,7 +411,7 @@
 	}
 	wav->packet = (unsigned char *)malloc(wav->blockAlign);
 	len -= 6;
-	    
+
 	wav->samples = (short *)malloc(wChannels*wav->samplesPerBlock*sizeof(short));
 
 	/* SJB: will need iCoefs later for adpcm.c */
@@ -418,7 +434,7 @@
 
 	wExtSize = rshort(ft);
 	wav->samplesPerBlock = rshort(ft);
-	wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;
+	wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;/* FIXME */
 	wav->packet = (unsigned char *)malloc(wav->blockAlign);
 	len -= 4;
 
@@ -479,7 +495,7 @@
 	len--;
     }
 
-    /* for ADPCM formats, there's a useless(?) 'fact' chunk before
+    /* for ADPCM formats, there's a 'fact' chunk before
      * the upcoming 'data' chunk */
 
     /* Now look for the wave data chunk */
@@ -509,9 +525,9 @@
 	wav->numSamples = (((data_length / wav->blockAlign) * wav->samplesPerBlock) * ft->info.channels);
 	/* 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->numSamples += 
+		((data_length % wav->blockAlign) - (6 * ft->info.channels)) * ft->info.channels;
+	/*report("datalen %d, numSamples %d",data_length, wav->numSamples);*/
 	wav->blockSamplesRemaining = 0;	       /* Samples left in buffer */
 	break;
 
@@ -576,7 +592,7 @@
 		    if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM)
 			wav->blockSamplesRemaining = ImaAdpcmReadBlock(ft);
 		    else
-			wav->blockSamplesRemaining = MsAdpcmReadBlock(ft);
+			wav->blockSamplesRemaining = AdpcmReadBlock(ft);
 		    if (wav->blockSamplesRemaining == 0)
 		    {
 			/* Don't try to read any more samples */
@@ -656,7 +672,9 @@
 	wav->packet = NULL;
 	wav->samples = NULL;
 	wav->iCoefs = NULL;
-	if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM) {
+	if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM ||
+	   wav->formatTag == WAVE_FORMAT_ADPCM)
+	{
 	    int ch, sbsize;
 	    /* #channels already range-checked for overflow in wavwritehdr() */
 	    for (ch=0; ch<ft->info.channels; ch++)
@@ -682,13 +700,13 @@
 	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 */
-        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 */
+	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 */
+	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 */
 	ULONG  bytespersample; 			/* bytes per sample (per channel) */
 
 	/* Needed for rawwrite() */
@@ -741,24 +759,40 @@
 		case ULAW:
 			wFormatTag = WAVE_FORMAT_MULAW;
 			break;
-		case ADPCM:
-		        warn("Experimental support writing IMA_ADPCM style.\n");
+		case IMA_ADPCM:
+			/* warn("Experimental support writing IMA_ADPCM style.\n"); */
 			wFormatTag = WAVE_FORMAT_IMA_ADPCM;
 			wBitsPerSample = 4;
 			fmtsize += 4;  /* plus ExtData */
 			factsize = 12; /* fact chunk   */
 			break;
+		case ADPCM:
+			/* warn("Experimental support writing ADPCM style.\n"); */
+			wFormatTag = WAVE_FORMAT_ADPCM;
+			wBitsPerSample = 4;
+			fmtsize += 2+4+4*7;  /* plus ExtData */
+			factsize = 12;       /* fact chunk   */
+			break;
 	}
 	wav->formatTag = wFormatTag;
 	
 	wSamplesPerSecond = ft->info.rate;
 	wChannels = ft->info.channels;
-	if (wFormatTag == WAVE_FORMAT_IMA_ADPCM) {
+	switch (wFormatTag)
+	{
+	case WAVE_FORMAT_IMA_ADPCM:
 	    if (wChannels>16)
 	    	fail("Channels(%d) must be <= 16\n",wChannels);
 	    bytespersample = 2;
 	    wBlockAlign = wChannels * 64; /* reasonable default */
-	} else {
+	    break;
+	case WAVE_FORMAT_ADPCM:
+	    if (wChannels>16)
+	    	fail("Channels(%d) must be <= 16\n",wChannels);
+	    bytespersample = 2;
+	    wBlockAlign = wChannels * 128; /* reasonable default */
+	    break;
+	default:
 	    bytespersample = (wBitsPerSample + 7)/8;
 	    wBlockAlign = wChannels * bytespersample;
 	}
@@ -767,12 +801,15 @@
 	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->dataLength; /* FIXME: not really */
-	    else
-		data_length = bytespersample * wav->numSamples;
-	}
+		switch(wFormatTag)
+		{
+	    	case WAVE_FORMAT_ADPCM:
+	    	case WAVE_FORMAT_IMA_ADPCM:
+		    data_length = wav->dataLength;
+		    break;
+		default:
+		    data_length = bytespersample * wav->numSamples;
+		}
 
 	/* figured out header info, so write it */
 	fputs("RIFF", ft->fp);
@@ -788,7 +825,7 @@
 	wshort(ft, wBitsPerSample); /* end of info common to all fmts */
 	switch (wFormatTag)
 	{
-	int nsamp;
+	int i,nsamp;
 	case WAVE_FORMAT_IMA_ADPCM:
 	    wshort(ft, 2);		/* Ext fmt data length */
 	    wav->samplesPerBlock = ((wBlockAlign - 4*wChannels)/(4*wChannels))*8 + 1;
@@ -799,6 +836,22 @@
 	    nsamp = (second_header)? wav->numSamples : 0x7fffffffL;
 	    wlong(ft, nsamp);
 	    break;
+	case WAVE_FORMAT_ADPCM:
+	    wshort(ft, 4+4*7);		/* Ext fmt data length */
+	    wav->samplesPerBlock = 2*(wBlockAlign - 7*wChannels)/wChannels + 2;
+	    wshort(ft, wav->samplesPerBlock);
+	    wshort(ft, 7); /* nCoefs */
+	    for (i=0; i<7; i++) {
+	      wshort(ft, iCoef[i][0]);
+	      wshort(ft, iCoef[i][1]);
+	    }
+
+	    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:
 	}
 
@@ -824,6 +877,7 @@
 	switch (wav->formatTag)
 	{
 	case WAVE_FORMAT_IMA_ADPCM:
+	case WAVE_FORMAT_ADPCM:
 	    while (len>0) {
 		short *p = wav->samplePtr;
 		short *top = wav->sampleTop;
@@ -834,11 +888,16 @@
 		   *p++ = ((*buf++) + 0x8000) >> 16;
 
 		wav->samplePtr = p;
-		if (p == wav->sampleTop)
-		    ImaAdpcmWriteBlock(ft);
+		if (p == wav->sampleTop) {
+		    if (wav->formatTag==WAVE_FORMAT_IMA_ADPCM)
+			ImaAdpcmWriteBlock(ft);
+		    else
+			AdpcmWriteBlock(ft);
+		}
 
 	    }
 	    break;
+
 	default:
 	    wav->numSamples += len;
 	    rawwrite(ft, buf, len);
@@ -850,12 +909,17 @@
 {
 	wav_t	wav = (wav_t) ft->priv;
 	/* Call this to flush out any remaining data. */
-	if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM) {
+	switch (wav->formatTag)
+	{
+	case WAVE_FORMAT_IMA_ADPCM:
 	    ImaAdpcmWriteBlock(ft);
-	} else {
+	    break;
+	case WAVE_FORMAT_ADPCM:
+	    AdpcmWriteBlock(ft);
+	    break;
+	default:
 	    rawstopwrite(ft);
 	}
-    	
 	if (wav->packet) free(wav->packet);
  	if (wav->samples) free(wav->samples);
  	if (wav->iCoefs) free(wav->iCoefs);