ref: f8ce704d16fe11b50067f4397674f703cfa4ae54
parent: 613f50d018d73308428dda8c610066a726e1a95e
author: cbagwell <cbagwell>
date: Tue Nov 23 16:28:31 EST 1999
Adding GSM WAV support plus other bug fixes.
--- a/Changelog
+++ b/Changelog
@@ -6,12 +6,18 @@
sox-12.17
---------
+ o Sox can now read and write w98 compatible gsm .wav files,
+ if compiled properly with libgsm. Thanks go to Stuart
+ Daines <sjd.u-net.com> for the gsm-wav patches.
+ This is new, and relatively untested. See -g format option.
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.
+ options in manpage.
o General changes to wav.c for writing additional wav formats.
+ Reading wave headers: more consistency checks.
+ Writing wave headers: fixes for w98.
o Speedups to adpcm read routines, new codex versions are
- now in ima_rw.c and adpcm.c.
+ 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.
--- a/README
+++ b/README
@@ -183,6 +183,9 @@
Stan Brooks stabro@megsinet.net
Rewrite of resample and polyphase code.
DSP filter effect. Some test code/scripts.
+ Stuart Daines <sjd.u-net.com>
+ Patches for r/w support of gsm-encoded wav files,
+ Cleanup of wav.c.
Chris Bagwell cbagwell@sprynet.com
OSS and Sun players, bugfixes, ADPCM support,
patch collection and maintance.
--- a/src/adpcm.c
+++ b/src/adpcm.c
@@ -368,7 +368,7 @@
obuff[ch] = kmin;
}
-void AdpcmMashI(
+void AdpcmBlockMashI(
int chans, /* total channels */
const SAMPL *ip, /* ip[n*chans] is interleaved input samples */
int n, /* samples to encode PER channel */
--- a/src/adpcm.h
+++ b/src/adpcm.h
@@ -22,7 +22,7 @@
int n /* samples to decode PER channel, REQUIRE n % 8 == 1 */
);
-extern void AdpcmMashI(
+extern void AdpcmBlockMashI(
int chans, /* total channels */
const SAMPL *ip, /* ip[n*chans] is interleaved input samples */
int n, /* samples to encode PER channel, REQUIRE */
--- a/src/ima_rw.c
+++ b/src/ima_rw.c
@@ -258,31 +258,11 @@
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(
+#ifdef __GNUC__
+inline
+#endif
+static void ImaMashChannel(
int ch, /* channel number to encode, REQUIRE 0 <= ch < chans */
int chans, /* total channels */
const SAMPL *ip, /* ip[] is interleaved input samples */
@@ -336,6 +316,21 @@
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); */
+}
+
+/* mash one block. if you want to use opt>0, 9 is a reasonable value */
+void ImaBlockMashI(
+ 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 ch;
+ for (ch=0; ch<chans; ch++)
+ ImaMashChannel(ch, chans, ip, n, st+ch, obuff, opt);
}
#if 0
--- a/src/ima_rw.h
+++ b/src/ima_rw.h
@@ -54,12 +54,12 @@
int n /* samples to decode PER channel, REQUIRE n % 8 == 1 */
);
-extern void ImaMashChannel(
- int ch, /* channel number to encode, REQUIRE 0 <= ch < chans */
+/* mash one block. if you want to use opt>0, 9 is a reasonable value */
+extern void ImaBlockMashI(
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 */
+ int *st, /* input/output state[chans], REQUIRE 0 <= st[ch] <= ISSTMAX */
u_char *obuff, /* output buffer[blockAlign] */
int opt /* non-zero allows some cpu-intensive code to improve output */
);
--- a/src/st.h
+++ b/src/st.h
@@ -27,6 +27,14 @@
#define ULONG unsigned long
#endif
+#ifdef __GNUC__
+#define NORET __attribute__((noreturn))
+#define INLINE inline
+#else
+#define NORET
+#define INLINE
+#endif
+
/*
* Handler structure for each format.
*/
@@ -256,10 +264,11 @@
#endif
double swapd(P1(double d)); /* Swap double */
-void report(P2(char *, ...)), warn(P2(char *, ...)),
- fail(P2(char *, ...));
-
/* util.c */
+void report(P2(char *, ...));
+void warn(P2(char *, ...));
+void fail(P2(char *, ...))NORET;
+
void geteffect(P1(eff_t));
void gettype(P1(ft_t));
void checkformat(P1(ft_t));
--- a/src/wav.c
+++ b/src/wav.c
@@ -8,7 +8,14 @@
*
* Change History:
*
- * November 22, 1999 - Stan Brooks (stabro@megsinet.com)
+ * November 23, 1999 - Stan Brooks (stabro@megsinet.com)
+ * Merged in gsm support patches from Stuart Daines...
+ * Since we had simultaneously made similar changes in
+ * wavwriteheader() and wavstartread(), this was some
+ * work. Hopefully the result is cleaner than either
+ * version, and nothing broke.
+ *
+ * November 20, 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,
@@ -67,7 +74,13 @@
#include "wav.h"
#include "ima_rw.h"
#include "adpcm.h"
+#ifdef HAVE_LIBGSM
+#include "gsm.h"
+#endif
+#undef PAD_NSAMPS
+/* #define PAD_NSAMPS */
+
/* Private data for .wav file */
typedef struct wavstuff {
LONG numSamples;
@@ -87,8 +100,22 @@
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 */
+
+ /* following used by GSM 6.10 wav */
+#ifdef HAVE_LIBGSM
+ gsm gsmhandle;
+ gsm_signal *gsmsample;
+ int gsmindex;
+ int gsmbytecount;
+#endif
} *wav_t;
+/*
+#if sizeof(struct wavstuff) > PRIVSIZE
+# warn "Uh-Oh"
+#endif
+*/
+
static char *wav_format_str();
LONG rawread(P3(ft_t, LONG *, LONG));
@@ -111,9 +138,9 @@
{
/* 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 quiet. */
+ /* padded but I guess this is better than trailing quiet. */
if (bytesRead >= (4 * ft->info.channels))
- {
+ { /* SJB: FIXME this is incorrect */
samplesThisBlock = (wav->blockAlign - (3 * ft->info.channels));
}
else
@@ -135,32 +162,6 @@
}
-static void ImaAdpcmWriteBlock(ft)
-ft_t ft;
-{
- wav_t wav = (wav_t) ft->priv;
- int chans, ch, 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 */
- for (ch=0; ch<chans; ch++)
- ImaMashChannel(ch, chans, wav->samples, wav->samplesPerBlock, &wav->state[ch], wav->packet, 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;
- }
-}
-
/****************************************************************************/
/* MS ADPCM Support Functions Section */
/****************************************************************************/
@@ -183,9 +184,9 @@
{
/* 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 than trailing quite. */
if (bytesRead >= (7 * ft->info.channels))
- {
+ { /* SJB: FIXME this is incorrect */
samplesThisBlock = (wav->blockAlign - (6 * ft->info.channels));
}
else
@@ -205,7 +206,10 @@
return samplesThisBlock;
}
-static void AdpcmWriteBlock(ft)
+/****************************************************************************/
+/* Common ADPCM Support Function */
+/****************************************************************************/
+static void xxxAdpcmWriteBlock(ft)
ft_t ft;
{
wav_t wav = (wav_t) ft->priv;
@@ -219,22 +223,198 @@
/* 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);
-
+ if (wav->formatTag == WAVE_FORMAT_ADPCM) {
+ AdpcmBlockMashI(chans, wav->samples, wav->samplesPerBlock, wav->state, wav->packet, wav->blockAlign,9);
+ }else{ /* WAVE_FORMAT_IMA_ADPCM */
+ ImaBlockMashI(chans, wav->samples, wav->samplesPerBlock, wav->state, wav->packet, 9);
+ }
/* write the compressed packet */
- fwrite(wav->packet, wav->blockAlign, 1, ft->fp); /* FIXME: check return value */
+ if (fwrite(wav->packet, wav->blockAlign, 1, ft->fp) != 1)
+ fail("write error");
/* update lengths and samplePtr */
wav->dataLength += wav->blockAlign;
+#ifndef PAD_NSAMPS
wav->numSamples += ct/chans;
+#else
+ wav->numSamples += wav->samplesPerBlock;
+#endif
wav->samplePtr = wav->samples;
}
}
/****************************************************************************/
+/* WAV GSM6.10 support functions */
+/****************************************************************************/
+#ifdef HAVE_LIBGSM
+/* create the gsm object, malloc buffer for 160*2 samples */
+void wavgsminit(ft)
+ft_t ft;
+{
+ int valueP=1;
+ wav_t wav = (wav_t) ft->priv;
+ wav->gsmbytecount=0;
+ wav->gsmhandle=gsm_create();
+ if (!wav->gsmhandle)
+ fail("cannot create GSM object");
+
+ if(gsm_option(wav->gsmhandle,GSM_OPT_WAV49,&valueP) == -1){
+ fail("error setting gsm_option for WAV49 format. Recompile gsm library with -DWAV49 option and relink sox");
+ }
+
+ wav->gsmsample=malloc(sizeof(gsm_signal)*160*2);
+ if (wav->gsmsample == NULL){
+ fail("error allocating memory for gsm buffer");
+ }
+ wav->gsmindex=0;
+}
+
+/*destroy the gsm object and free the buffer */
+void wavgsmdestroy(ft)
+ft_t ft;
+{
+ wav_t wav = (wav_t) ft->priv;
+ gsm_destroy(wav->gsmhandle);
+ free(wav->gsmsample);
+}
+
+LONG wavgsmread(ft, buf, len)
+ft_t ft;
+LONG *buf, len;
+{
+ wav_t wav = (wav_t) ft->priv;
+ int done=0;
+ int bytes;
+ gsm_frame frame;
+
+ /* copy out any samples left from the last call */
+ while(wav->gsmindex && (wav->gsmindex<160*2) && (done < len))
+ buf[done++]=LEFT(wav->gsmsample[wav->gsmindex++],16);
+
+ /* read and decode loop, possibly leaving some samples in wav->gsmsample */
+ while (done < len) {
+ wav->gsmindex=0;
+ /*read the long 33 byte half */
+ bytes = fread(frame,1,sizeof(frame),ft->fp);
+ if (bytes <=0)
+ return done;
+ if (bytes<sizeof(frame))
+ fail("invalid wav gsm frame size: %d bytes",bytes);
+ if(gsm_decode(wav->gsmhandle,frame, wav->gsmsample)<0)
+ fail("error during gsm decode");
+
+ /*read the short 32 byte half */
+ bytes = fread(frame,1,sizeof(frame)-1,ft->fp);
+ if (bytes <=0)
+ return done;
+ if (bytes<sizeof(frame)-1)
+ fail("invalid wav gsm frame size: %d bytes",bytes);
+ if(gsm_decode(wav->gsmhandle,frame, &(wav->gsmsample[160]))<0)
+ fail("error during gsm decode");
+
+
+ while ((wav->gsmindex <160*2) && (done < len)){
+ buf[done++]=LEFT(wav->gsmsample[(wav->gsmindex)++],16);
+ }
+ }
+
+ return done;
+}
+
+void wavgsmwrite(ft, buf, len)
+ft_t ft;
+LONG *buf, len;
+{
+ wav_t wav = (wav_t) ft->priv;
+
+ int done = 0;
+ gsm_frame frame;
+
+ while (done < len) {
+ while ((wav->gsmindex < 160*2) && (done < len)){
+ wav->gsmsample[(wav->gsmindex)++] = RIGHT(buf[done++], 16);
+ }
+ if (wav->gsmindex < 160*2){
+ return;
+ }
+
+ /*encode the even half and write short (32 byte) frame */
+ gsm_encode(wav->gsmhandle, wav->gsmsample, frame);
+ if (fwrite(frame, 1, sizeof(frame)-1, ft->fp) != sizeof(frame)-1)
+ fail("write error");
+ wav->gsmbytecount += sizeof(frame)-1;
+
+ /*encode the odd half and write long (33 byte) frame */
+ gsm_encode(wav->gsmhandle, &(wav->gsmsample[160]), frame);
+ if (fwrite(frame, 1, sizeof(frame), ft->fp) != sizeof(frame))
+ fail("write error");
+ wav->gsmbytecount += sizeof(frame);
+ wav->gsmindex = 0;
+ }
+
+}
+
+void wavgsmstopwrite(ft)
+ft_t ft;
+{
+ gsm_frame frame;
+ wav_t wav = (wav_t) ft->priv;
+ if (wav->gsmindex){
+ while(wav->gsmindex<160*2){
+ wav->gsmsample[wav->gsmindex++]=0;
+ }
+
+ /*encode the even half and write short (32 byte) frame */
+ gsm_encode(wav->gsmhandle, wav->gsmsample, frame);
+ if (fwrite(frame, 1, sizeof(frame)-1, ft->fp) != sizeof(frame)-1)
+ fail("write error");
+ wav->gsmbytecount += sizeof(frame)-1;
+
+ /*encode the odd half and write long (33 byte) frame */
+ gsm_encode(wav->gsmhandle, &(wav->gsmsample[160]), frame);
+ if (fwrite(frame, 1, sizeof(frame), ft->fp) != sizeof(frame))
+ fail("write error");
+ wav->gsmbytecount += sizeof(frame);
+
+ /* pad output to an even number of bytes */
+ if (wav->gsmbytecount & 0x1){
+ if(fputc(0,ft->fp))
+ fail("write error");
+ wav->gsmbytecount += 1;
+ }
+ }
+ wavgsmdestroy(ft);
+}
+#endif /*ifdef out gsm code */
+/****************************************************************************/
/* General Sox WAV file code */
/****************************************************************************/
+static void fSkip(FILE *fp, ULONG len)
+{ /* FIXME: this should also check ferror(fp) */
+ while (len > 0 && !feof(fp))
+ {
+ getc(fp);
+ len--;
+ }
+}
+
+static ULONG findChunk(ft_t ft, const char *Label)
+{
+ char magic[4];
+ ULONG len;
+ for (;;)
+ {
+ if (fread(magic, 1, 4, ft->fp) != 4)
+ fail("WAVE file has missing %s chunk", Label);
+ len = rlong(ft);
+ if (strncmp(Label, magic, 4) == 0)
+ break; /* Found the data chunk */
+
+ fSkip(ft->fp, len); /* skip to next chunk */
+ }
+ return len;
+}
+
/*
* Do anything required before you start reading samples.
* Read file header.
@@ -252,13 +432,15 @@
char *endptr;
/* wave file characteristics */
+ ULONG wRiffLength;
unsigned short wChannels; /* number of channels */
ULONG wSamplesPerSecond; /* samples per second per channel */
ULONG wAvgBytesPerSec; /* estimate of bytes per second needed */
unsigned short wBitsPerSample; /* bits per sample */
- unsigned short wExtSize = 0; /* extended field for ADPCM */
+ unsigned short wFmtSize;
+ unsigned short wExtSize = 0; /* extended field for non-PCM */
- ULONG data_length; /* length of sound data in bytes */
+ ULONG wDataLength; /* length of sound data in bytes */
ULONG bytespersample; /* bytes per sample (per channel */
/* This is needed for rawread() */
@@ -274,33 +456,26 @@
if ( fread(magic, 1, 4, ft->fp) != 4 || strncmp("RIFF", magic, 4))
fail("WAVE: RIFF header not found");
- len = rlong(ft);
+ wRiffLength = rlong(ft);
if ( fread(magic, 1, 4, ft->fp) != 4 || strncmp("WAVE", magic, 4))
fail("WAVE header not found");
/* Now look for the format chunk */
- for (;;)
- {
- if ( fread(magic, 1, 4, ft->fp) != 4 )
- fail("WAVE file missing fmt spec");
- len = rlong(ft);
- if (strncmp("fmt ", magic, 4) == 0)
- break; /* Found the format chunk */
-
- /* skip to next chunk */
- while (len > 0 && !feof(ft->fp))
- {
- getc(ft->fp);
- len--;
- }
- }
- /* we only get here if we just read the magic "fmt " */
- if ( len < 16 )
+ wFmtSize = len = findChunk(ft, "fmt ");
+ /* findChunk() only returns if chunk was found */
+
+ if (wFmtSize < 16)
fail("WAVE file fmt chunk is too short");
wav->formatTag = rshort(ft);
- len -= 2;
+ wChannels = rshort(ft);
+ wSamplesPerSecond = rlong(ft);
+ wAvgBytesPerSec = rlong(ft); /* Average bytes/second */
+ wav->blockAlign = rshort(ft); /* Block align */
+ wBitsPerSample = rshort(ft); /* bits per sample per channel */
+ len -= 16;
+
switch (wav->formatTag)
{
case WAVE_FORMAT_UNKNOWN:
@@ -307,14 +482,20 @@
fail("WAVE file is in unsupported Microsoft Official Unknown format.");
case WAVE_FORMAT_PCM:
- /* Default (-1) depends on sample size. Set that later on. */
+ /* Default (-1) depends on sample size. Set that later on. */
if (ft->info.style != -1 && ft->info.style != UNSIGNED &&
ft->info.style != SIGN2)
warn("User options overriding style read in .wav header");
break;
- case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_IMA_ADPCM:
+ if (ft->info.style == -1 || ft->info.style == IMA_ADPCM)
+ ft->info.style = IMA_ADPCM;
+ else
+ warn("User options overriding style read in .wav header");
+ break;
+
+ case WAVE_FORMAT_ADPCM:
if (ft->info.style == -1 || ft->info.style == ADPCM)
ft->info.style = ADPCM;
else
@@ -347,7 +528,15 @@
case WAVE_FORMAT_DOLBY_AC2:
fail("Sorry, this WAV file is in Dolby AC2 format.");
case WAVE_FORMAT_GSM610:
- fail("Sorry, this WAV file is in GSM 6.10 format.");
+#ifdef HAVE_LIBGSM
+ if (ft->info.style == -1 || ft->info.style == GSM )
+ ft->info.style = GSM;
+ else
+ warn("User options overriding style read in .wav header");
+ break;
+#else
+ fail("Sorry, this WAV file is in GSM6.10 format and no GSM support present, recompile sox with gsm library");
+#endif
case WAVE_FORMAT_ROCKWELL_ADPCM:
fail("Sorry, this WAV file is in Rockwell ADPCM format.");
case WAVE_FORMAT_ROCKWELL_DIGITALK:
@@ -367,8 +556,6 @@
default: fail("WAV file has unknown format type of %x",wav->formatTag);
}
- wChannels = rshort(ft);
- len -= 2;
/* User options take precedence */
if (ft->info.channels == -1 || ft->info.channels == wChannels)
ft->info.channels = wChannels;
@@ -375,33 +562,39 @@
else
warn("User options overriding channels read in .wav header");
- wSamplesPerSecond = rlong(ft);
- len -= 4;
if (ft->info.rate == 0 || ft->info.rate == wSamplesPerSecond)
ft->info.rate = wSamplesPerSecond;
else
warn("User options overriding rate read in .wav header");
- wAvgBytesPerSec = rlong(ft); /* Average bytes/second */
- wav->blockAlign = rshort(ft); /* Block align */
- len -= 6;
- /* bits per sample per channel */
- wBitsPerSample = rshort(ft);
- len -= 2;
-
wav->iCoefs = NULL;
wav->packet = NULL;
wav->samples = NULL;
- /* ADPCM formats have extended fmt chunk. Check for those cases. */
+ /* non-PCM formats have extended fmt chunk. Check for those cases. */
+ if (wav->formatTag != WAVE_FORMAT_PCM) {
+ if (len >= 2) {
+ wExtSize = rshort(ft);
+ len -= 2;
+ } else {
+ warn("wave header missing FmtExt chunk");
+ }
+ }
+
+ if (wExtSize > len)
+ fail("wave header error: wExtSize inconsistent with wFmtLen");
+
switch (wav->formatTag)
{
case WAVE_FORMAT_ADPCM:
+ if (wExtSize < 4)
+ fail("wave header error: format[%s] expects wExtSize >= %d",
+ wav_format_str(wav->formatTag), 4);
+
if (wBitsPerSample != 4)
fail("Can only handle 4-bit MS ADPCM in wav files");
- wExtSize = rshort(ft);
wav->samplesPerBlock = rshort(ft);
wav->bytesPerBlock = ((wav->samplesPerBlock-2)*ft->info.channels + 1)/2
+ 7*ft->info.channels;
@@ -410,8 +603,11 @@
fail("ADPCM file nCoefs (%.4hx) makes no sense\n", wav->nCoefs);
}
wav->packet = (unsigned char *)malloc(wav->blockAlign);
- len -= 6;
+ len -= 4;
+ if (wExtSize < 4 + 4*wav->nCoefs)
+ fail("wave header error: wExtSize(%d) too small for nCoefs(%d)", wExtSize, wav->nCoefs);
+
wav->samples = (short *)malloc(wChannels*wav->samplesPerBlock*sizeof(short));
/* SJB: will need iCoefs later for adpcm.c */
@@ -426,22 +622,44 @@
}
bytespersample = WORD; /* AFTER de-compression */
- break;
+ break;
case WAVE_FORMAT_IMA_ADPCM:
+ if (wExtSize < 2)
+ fail("wave header error: format[%s] expects wExtSize >= %d",
+ wav_format_str(wav->formatTag), 2);
+
if (wBitsPerSample != 4)
fail("Can only handle 4-bit IMA ADPCM in wav files");
- wExtSize = rshort(ft);
wav->samplesPerBlock = rshort(ft);
wav->bytesPerBlock = (wav->samplesPerBlock + 7)/2 * ft->info.channels;/* FIXME */
wav->packet = (unsigned char *)malloc(wav->blockAlign);
- len -= 4;
+ len -= 2;
wav->samples = (short *)malloc(wChannels*wav->samplesPerBlock*sizeof(short));
bytespersample = WORD; /* AFTER de-compression */
break;
+
+#ifdef HAVE_LIBGSM
+ /* GSM formats have extended fmt chunk. Check for those cases. */
+ case WAVE_FORMAT_GSM610:
+ if (wExtSize < 2)
+ fail("wave header error: format[%s] expects wExtSize >= %d",
+ wav_format_str(wav->formatTag), 2);
+ wav->samplesPerBlock = rshort(ft);
+ if (wav->blockAlign != 65)
+ fail("wave header error: format[%s] expects blockAlign(%d) = %d",
+ wav_format_str(wav->formatTag), wav->blockAlign, 65);
+ if (wav->samplesPerBlock != 320)
+ fail("wave header error: format[%s] expects samplesPerBlock(%d) = %d",
+ wav_format_str(wav->formatTag), wav->samplesPerBlock, 320);
+ bytespersample = WORD; /* AFTER de-compression */
+ len -= 2;
+ break;
+#endif
+
default:
bytespersample = (wBitsPerSample + 7)/8;
@@ -488,33 +706,15 @@
fail("Sorry, don't understand .wav size");
}
- /* Skip past the rest of any left over fmt chunk */
- while (len > 0 && !feof(ft->fp))
- {
- getc(ft->fp);
- len--;
- }
+ /* Skip anything left over from fmt chunk */
+ fSkip(ft->fp, len);
- /* for ADPCM formats, there's a 'fact' chunk before
+ /* for non-PCM formats, there's a 'fact' chunk before
* the upcoming 'data' chunk */
/* Now look for the wave data chunk */
- for (;;)
- {
- if ( fread(magic, 1, 4, ft->fp) != 4 )
- fail("WAVE file has missing data chunk");
- len = rlong(ft);
- if (strncmp("data", magic, 4) == 0)
- break; /* Found the data chunk */
-
- while (len > 0 && !feof(ft->fp)) /* skip to next chunk */
- {
- getc(ft->fp);
- len--;
- }
- }
-
- data_length = len;
+ wDataLength = len = findChunk(ft, "data");
+ /* findChunk() only returns if chunk was found */
switch (wav->formatTag)
{
@@ -522,12 +722,12 @@
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);
+ wav->numSamples = (((wDataLength / 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 % wav->blockAlign) - (6 * ft->info.channels)) * ft->info.channels;
- /*report("datalen %d, numSamples %d",data_length, wav->numSamples);*/
+ ((wDataLength % wav->blockAlign) - (6 * ft->info.channels)) * ft->info.channels;
+ /*report("datalen %d, numSamples %d",wDataLength, wav->numSamples);*/
wav->blockSamplesRemaining = 0; /* Samples left in buffer */
break;
@@ -534,18 +734,24 @@
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);
+ wav->numSamples = (((wDataLength / wav->blockAlign) * wav->samplesPerBlock) * ft->info.channels);
/* Next, for any partial blocks, substract overhead from it and it
will leave # of samples to read. */
- wav->numSamples += ((data_length - ((data_length/wav->blockAlign)
- *wav->blockAlign))
- - (3 * ft->info.channels)) * ft->info.channels;
+ wav->numSamples +=
+ ((wDataLength % wav->blockAlign) - (3 * ft->info.channels)) * ft->info.channels;
wav->blockSamplesRemaining = 0; /* Samples left in buffer */
initImaTable();
break;
+#ifdef HAVE_LIBGSM
+ case WAVE_FORMAT_GSM610:
+ wav->numSamples = (((wDataLength / wav->blockAlign) * wav->samplesPerBlock) * ft->info.channels);
+ wavgsminit(ft);
+ break;
+#endif
+
default:
- wav->numSamples = data_length/ft->info.size; /* total samples */
+ wav->numSamples = wDataLength/ft->info.size; /* total samples */
}
@@ -553,17 +759,34 @@
wav_format_str(wav->formatTag), ft->info.channels,
wChannels == 1 ? "" : "s", wSamplesPerSecond);
report(" %d byte/sec, %d block align, %d bits/samp, %u data bytes",
- wAvgBytesPerSec, wav->blockAlign, wBitsPerSample, data_length);
+ wAvgBytesPerSec, wav->blockAlign, wBitsPerSample, wDataLength);
/* 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",
+ switch (wav->formatTag)
+ {
+ case WAVE_FORMAT_ADPCM:
+ report(" %d Extsize, %d Samps/block, %d bytes/block %d Num Coefs",
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",
+ break;
+
+ case WAVE_FORMAT_IMA_ADPCM:
+ report(" %d Extsize, %d Samps/block, %d bytes/block",
wExtSize,wav->samplesPerBlock,wav->bytesPerBlock);
+ break;
+
+#ifdef HAVE_LIBGSM
+ case WAVE_FORMAT_GSM610:
+ report("GSM .wav: %d Extsize, %d Samps/block, %d samples",
+ wExtSize,wav->samplesPerBlock,wav->numSamples);
+ break;
+#endif
+
+ default:
+ }
+
}
+
/*
* Read up to len samples from file.
* Convert to signed longs.
@@ -584,6 +807,7 @@
/* read as much as possible and return quickly. */
switch (ft->info.style)
{
+ case IMA_ADPCM:
case ADPCM:
done = 0;
while (done < len) { /* Still want data? */
@@ -625,6 +849,13 @@
}
break;
+#ifdef HAVE_LIBGSM
+ case GSM:
+ done = wavgsmread(ft, buf, len);
+ if (done == 0 && wav->numSamples != 0)
+ warn("Premature EOF on .wav input file");
+ break;
+#endif
default: /* not ADPCM style */
done = rawread(ft, buf, len);
/* If software thinks there are more samples but I/O */
@@ -650,8 +881,20 @@
if (wav->samples) free(wav->samples);
if (wav->iCoefs) free(wav->iCoefs);
- /* Needed for rawread() */
- rawstopread(ft);
+ switch (ft->info.style)
+ {
+#ifdef HAVE_LIBGSM
+ case GSM:
+ wavgsmdestroy(ft);
+ break;
+#endif
+ case IMA_ADPCM:
+ case ADPCM:
+ break;
+ default:
+ /* Needed for rawread() */
+ rawstopread(ft);
+ }
}
void wavstartwrite(ft)
@@ -672,10 +915,13 @@
wav->packet = NULL;
wav->samples = NULL;
wav->iCoefs = NULL;
- if (wav->formatTag == WAVE_FORMAT_IMA_ADPCM ||
- wav->formatTag == WAVE_FORMAT_ADPCM)
+ switch (wav->formatTag)
{
- int ch, sbsize;
+ int ch, sbsize;
+ case WAVE_FORMAT_IMA_ADPCM:
+ initImaTable();
+ /* intentional case fallthru! */
+ case WAVE_FORMAT_ADPCM:
/* #channels already range-checked for overflow in wavwritehdr() */
for (ch=0; ch<ft->info.channels; ch++)
wav->state[ch] = 0;
@@ -684,35 +930,119 @@
wav->samples = (short *)malloc(sbsize*sizeof(short));
wav->sampleTop = wav->samples + sbsize;
wav->samplePtr = wav->samples;
- initImaTable();
+ break;
+
+#ifdef HAVE_LIBGSM
+ case WAVE_FORMAT_GSM610:
+ wavgsminit(ft);
+ break;
+#endif
+ default:
}
}
-#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 */
+/* wavwritehdr: write .wav headers as follows:
+
+bytes variable description
+0 - 3 'RIFF'
+4 - 7 wRiffLength length of file minus the 8 byte riff header
+8 - 11 'WAVE'
+12 - 15 'fmt '
+16 - 19 wFmtSize length of format chunk minus 8 byte header
+20 - 21 wFormatTag identifies PCM, ULAW etc
+22 - 23 wChannels
+24 - 27 wSamplesPerSecond samples per second per channel
+28 - 31 wAvgBytesPerSec non-trivial for compressed formats
+32 - 33 wBlockAlign basic block size
+34 - 35 wBitsPerSample non-trivial for compressed formats
+
+PCM formats then go straight to the data chunk:
+36 - 39 'data'
+40 - 43 wDataLength length of data chunk minus 8 byte header
+44 - (wDataLength + 43) the data
+
+non-PCM formats must write an extended format chunk and a fact chunk:
+
+ULAW, ALAW formats:
+36 - 37 wExtSize = 0 the length of the format extension
+38 - 41 'fact'
+42 - 45 wFactSize = 4 length of the fact chunk minus 8 byte header
+46 - 49 wSamplesWritten actual number of samples written out
+50 - 53 'data'
+54 - 57 wDataLength length of data chunk minus 8 byte header
+58 - (wDataLength + 57) the data
+
+
+GSM6.10 format:
+36 - 37 wExtSize = 2 the length in bytes of the format-dependent extension
+38 - 39 320 number of samples per block
+40 - 43 'fact'
+44 - 47 wFactSize = 4 length of the fact chunk minus 8 byte header
+48 - 51 wSamplesWritten actual number of samples written out
+52 - 55 'data'
+56 - 59 wDataLength length of data chunk minus 8 byte header
+60 - (wDataLength + 59) the data
+(+ a padding byte if wDataLength is odd)
+
+
+note that header contains (up to) 3 separate ways of describing the
+length of the file, all derived here from the number of (input)
+samples wav->numSamples in a way that is non-trivial for the blocked
+and padded compressed formats:
+
+wRiffLength - (riff header) the length of the file, minus 8
+wSamplesWritten - (fact header) the number of samples written (after padding
+ to a complete block eg for GSM)
+wDataLength - (data chunk header) the number of (valid) data bytes written
+
+*/
+
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 */
- 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) */
+ /* variables written to wav file header */
+ /* RIFF header */
+ ULONG wRiffLength ; /* length of file after 8 byte riff header */
+ /* fmt chunk */
+ ULONG wFmtSize = 16; /* size field of the fmt chunk */
+ unsigned short wFormatTag = 0; /* data format */
+ unsigned short wChannels; /* number of channels */
+ ULONG wSamplesPerSecond; /* samples per second per channel*/
+ ULONG wAvgBytesPerSec=0; /* estimate of bytes per second needed */
+ unsigned short wBlockAlign=0; /* byte alignment of a basic sample block */
+ unsigned short wBitsPerSample=0; /* bits per sample */
+ /* fmt chunk extension (not PCM) */
+ unsigned short wExtSize=0; /* extra bytes in the format extension */
+ unsigned short wSamplesPerBlock; /* samples per channel per block */
+ /* wSamplesPerBlock and other things may go into format extension */
- /* Needed for rawwrite() */
- if (ft->info.style != ADPCM)
+ /* fact chunk (not PCM) */
+ ULONG wFactSize=4; /* length of the fact chunk */
+ ULONG wSamplesWritten=0; /* windows doesnt seem to use this*/
+
+ /* data chunk */
+ ULONG wDataLength=0x7ffff000L; /* length of sound data in bytes */
+ /* end of variables written to header */
+
+ /* internal variables, intermediate values etc */
+ ULONG bytespersample; /* (uncompressed) bytes per sample (per channel) */
+ ULONG blocksWritten = 0;
+
+ if (ft->info.style != ADPCM &&
+ ft->info.style != IMA_ADPCM &&
+ ft->info.style != GSM
+ )
rawstartwrite(ft);
+ wSamplesPerSecond = ft->info.rate;
+ wChannels = ft->info.channels;
+
+ if (wChannels == 0 || wChannels>64) /* FIXME: arbitrary upper limit */
+ fail("Channels(%d) out-of-range\n",wChannels);
+
switch (ft->info.size)
{
case BYTE:
@@ -719,8 +1049,7 @@
wBitsPerSample = 8;
if (ft->info.style != UNSIGNED &&
ft->info.style != ULAW &&
- ft->info.style != ALAW &&
- !second_header)
+ ft->info.style != ALAW)
{
warn("Only support unsigned, ulaw, or alaw with 8-bit data. Forcing to unsigned");
ft->info.style = UNSIGNED;
@@ -745,118 +1074,153 @@
break;
}
+ bytespersample = WORD; /* common default */
+ wSamplesPerBlock = 1; /* common default */
+
switch (ft->info.style)
{
case UNSIGNED:
- wFormatTag = WAVE_FORMAT_PCM;
- break;
case SIGN2:
wFormatTag = WAVE_FORMAT_PCM;
+ bytespersample = (wBitsPerSample + 7)/8;
+ wBlockAlign = wChannels * bytespersample;
break;
case ALAW:
wFormatTag = WAVE_FORMAT_ALAW;
+ bytespersample = BYTE;
+ wBlockAlign = wChannels;
break;
case ULAW:
wFormatTag = WAVE_FORMAT_MULAW;
+ bytespersample = BYTE;
+ wBlockAlign = wChannels;
break;
case IMA_ADPCM:
/* warn("Experimental support writing IMA_ADPCM style.\n"); */
+ if (wChannels>16)
+ fail("Channels(%d) must be <= 16\n",wChannels);
wFormatTag = WAVE_FORMAT_IMA_ADPCM;
+ wBlockAlign = wChannels * 64; /* reasonable default */
wBitsPerSample = 4;
- fmtsize += 4; /* plus ExtData */
- factsize = 12; /* fact chunk */
+ wExtSize = 2;
+ wSamplesPerBlock = ((wBlockAlign - 4*wChannels)/(4*wChannels))*8 + 1;
break;
case ADPCM:
/* warn("Experimental support writing ADPCM style.\n"); */
+ if (wChannels>16)
+ fail("Channels(%d) must be <= 16\n",wChannels);
wFormatTag = WAVE_FORMAT_ADPCM;
+ wBlockAlign = wChannels * 128; /* reasonable default */
wBitsPerSample = 4;
- fmtsize += 2+4+4*7; /* plus ExtData */
- factsize = 12; /* fact chunk */
+ wExtSize = 4+4*7; /* Ext fmt data length */
+ wSamplesPerBlock = 2*(wBlockAlign - 7*wChannels)/wChannels + 2;
break;
+ case GSM:
+#ifdef HAVE_LIBGSM
+ if (wChannels!=1)
+ fail("Channels(%d) must be == 1\n",wChannels);
+ wFormatTag = WAVE_FORMAT_GSM610;
+ /* wAvgBytesPerSec = 1625*(wSamplesPerSecond/8000.)+0.5; */
+ wBlockAlign=65;
+ wBitsPerSample=0; /* not representable as int */
+ wExtSize=2; /* length of format extension */
+ wSamplesPerBlock = 320;
+#else
+ fail("sorry, no GSM6.10 support, recompile sox with gsm library");
+#endif
+ break;
}
wav->formatTag = wFormatTag;
-
- wSamplesPerSecond = ft->info.rate;
- wChannels = ft->info.channels;
- 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 */
- 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;
- }
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 */
- switch(wFormatTag)
+ wav->samplesPerBlock = wSamplesPerBlock;
+
+ if (!second_header) { /* adjust for blockAlign */
+ blocksWritten = wDataLength/wBlockAlign;
+ wDataLength = blocksWritten * wBlockAlign;
+ wSamplesWritten = blocksWritten * wSamplesPerBlock;
+ } else { /* fixup with real length */
+ wSamplesWritten = wav->numSamples;
+ switch(wFormatTag)
{
case WAVE_FORMAT_ADPCM:
case WAVE_FORMAT_IMA_ADPCM:
- data_length = wav->dataLength;
+ wDataLength = wav->dataLength;
break;
+#ifdef HAVE_LIBGSM
+ case WAVE_FORMAT_GSM610:
+ /* intentional case fallthrough! */
+#endif
default:
- data_length = bytespersample * wav->numSamples;
+ wSamplesWritten /= wChannels; /* because how rawwrite()'s work */
+ blocksWritten = (wSamplesWritten+wSamplesPerBlock-1)/wSamplesPerBlock;
+ wDataLength = blocksWritten * wBlockAlign;
}
+ }
+#ifdef HAVE_LIBGSM
+ if (wFormatTag == WAVE_FORMAT_GSM610)
+ wDataLength = (wDataLength+1) & ~1; /*round up to even */
+#endif
+
+ if (wFormatTag != WAVE_FORMAT_PCM)
+ wFmtSize += 2+wExtSize; /* plus ExtData */
+
+ wRiffLength = 4 + (8+wFmtSize) + (8+wDataLength);
+ if (wFormatTag != WAVE_FORMAT_PCM) /* PCM omits the "fact" chunk */
+ wRiffLength += (8+wFactSize);
+
+ /* wAvgBytesPerSec <-- this is BEFORE compression, isn't it? */
+ /* if (wFormatTag != WAVE_FORMAT_GSM610) GSM set this above */
+ wAvgBytesPerSec = ft->info.rate * wChannels * bytespersample;
+
/* figured out header info, so write it */
fputs("RIFF", ft->fp);
- wlong(ft, data_length + WHDRSIZ1+fmtsize+factsize+DATASIZ1);/* Waveform chunk size: FIXUP(4) */
+ wlong(ft, wRiffLength);
fputs("WAVE", ft->fp);
fputs("fmt ", ft->fp);
- wlong(ft, fmtsize-8); /* fmt chunk size */
+ wlong(ft, wFmtSize);
wshort(ft, wFormatTag);
wshort(ft, wChannels);
wlong(ft, wSamplesPerSecond);
wlong(ft, wAvgBytesPerSec);
wshort(ft, wBlockAlign);
- wshort(ft, wBitsPerSample); /* end of info common to all fmts */
+ wshort(ft, wBitsPerSample); /* end info common to all fmts */
+
+ /* if not PCM, we need to write out wExtSize even if wExtSize=0 */
+ if (wFormatTag != WAVE_FORMAT_PCM)
+ wshort(ft,wExtSize);
+
switch (wFormatTag)
{
- int i,nsamp;
+ int i;
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);
+ wshort(ft, wSamplesPerBlock);
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, wSamplesPerBlock);
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;
+#ifdef HAVE_LIBGSM
+ case WAVE_FORMAT_GSM610:
+ wshort(ft, wSamplesPerBlock);
+ break;
+#endif
default:
}
+ /* if not PCM, write the 'fact' chunk */
+ if (wFormatTag != WAVE_FORMAT_PCM){
+ fputs("fact", ft->fp);
+ wlong(ft,wFactSize);
+ wlong(ft,wSamplesWritten);
+ }
+
fputs("data", ft->fp);
- wlong(ft, data_length); /* data chunk size: FIXUP(40) */
+ wlong(ft, wDataLength); /* data chunk size */
if (!second_header) {
report("Writing Wave file: %s format, %d channel%s, %d samp/sec",
@@ -864,10 +1228,23 @@
wChannels == 1 ? "" : "s", wSamplesPerSecond);
report(" %d byte/sec, %d block align, %d bits/samp",
wAvgBytesPerSec, wBlockAlign, wBitsPerSample);
- } else
- report("Finished writing Wave file, %u data bytes\n",data_length);
+ } else {
+ report("Finished writing Wave file, %u data bytes %u samples\n",
+ wDataLength,wav->numSamples);
+#ifdef HAVE_LIBGSM
+ if (wFormatTag == WAVE_FORMAT_GSM610){
+ report("GSM6.10 format: %u blocks %u padded samples %u padded data bytes\n",
+ blocksWritten, wSamplesWritten, wDataLength);
+ if (wav->gsmbytecount != wDataLength)
+ warn("help ! internal inconsistency - data_written %u gsmbytecount %u",
+ wDataLength, wav->gsmbytecount);
+
+ }
+#endif
+ }
}
+
void wavwrite(ft, buf, len)
ft_t ft;
LONG *buf, len;
@@ -888,18 +1265,20 @@
*p++ = ((*buf++) + 0x8000) >> 16;
wav->samplePtr = p;
- if (p == wav->sampleTop) {
- if (wav->formatTag==WAVE_FORMAT_IMA_ADPCM)
- ImaAdpcmWriteBlock(ft);
- else
- AdpcmWriteBlock(ft);
- }
+ if (p == wav->sampleTop)
+ xxxAdpcmWriteBlock(ft);
}
break;
- default:
+#ifdef HAVE_LIBGSM
+ case WAVE_FORMAT_GSM610:
wav->numSamples += len;
+ wavgsmwrite(ft, buf, len);
+ break;
+#endif
+ default:
+ wav->numSamples += len; /* must later be divided by wChannels */
rawwrite(ft, buf, len);
}
}
@@ -912,11 +1291,14 @@
switch (wav->formatTag)
{
case WAVE_FORMAT_IMA_ADPCM:
- ImaAdpcmWriteBlock(ft);
- break;
case WAVE_FORMAT_ADPCM:
- AdpcmWriteBlock(ft);
+ xxxAdpcmWriteBlock(ft);
break;
+#ifdef HAVE_LIBGSM
+ case WAVE_FORMAT_GSM610:
+ wavgsmstopwrite(ft);
+ break;
+#endif
default:
rawstopwrite(ft);
}