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);