diff decoders/wav.c @ 124:e46e31fdecfd

Restructured to handle multiple formats.
author Ryan C. Gordon <icculus@icculus.org>
date Mon, 08 Oct 2001 19:13:16 +0000
parents 40de367eb59e
children 1df5c106504e
line wrap: on
line diff
--- a/decoders/wav.c	Mon Oct 08 18:49:20 2001 +0000
+++ b/decoders/wav.c	Mon Oct 08 19:13:16 2001 +0000
@@ -66,37 +66,6 @@
     WAV_read        /*  read() method */
 };
 
-
-    /* this is what we store in our internal->decoder_private field... */
-typedef struct
-{
-    Sint32 bytesLeft;
-} wav_t;
-
-
-    /* Chunk management code... */
-
-#define riffID 0x46464952  /* "RIFF", in ascii. */
-#define waveID 0x45564157  /* "WAVE", in ascii. */
-
-
-#define fmtID  0x20746D66  /* "fmt ", in ascii. */
-
-#define FMT_NORMAL 1    /* Uncompressed waveform data. */
-
-typedef struct
-{
-    Uint32 chunkID;
-    Sint32 chunkSize;
-    Sint16 wFormatTag;
-    Uint16 wChannels;
-    Uint32 dwSamplesPerSec;
-    Uint32 dwAvgBytesPerSec;
-    Uint16 wBlockAlign;
-    Uint16 wBitsPerSample;
-} fmt_t;
-
-
 static int WAV_init(void)
 {
     return(1);  /* always succeeds. */
@@ -109,9 +78,59 @@
 } /* WAV_quit */
 
 
+
+    /* Chunk management code... */
+
+#define riffID 0x46464952  /* "RIFF", in ascii. */
+#define waveID 0x45564157  /* "WAVE", in ascii. */
+
+
+/*****************************************************************************
+ * The FORMAT chunk...                                                       *
+ *****************************************************************************/
+
+#define fmtID  0x20746D66  /* "fmt ", in ascii. */
+
+#define FMT_NORMAL 0x0001    /* Uncompressed waveform data.     */
+#define FMT_ADPCM  0x0002    /* ADPCM compressed waveform data. */
+
+typedef struct {
+    Uint16 iCoef1;
+    Uint16 iCoef2;
+} ADPCMCOEFSET;
+
+typedef struct S_FMT_T
+{
+    Uint32 chunkID;
+    Sint32 chunkSize;
+    Sint16 wFormatTag;
+    Uint16 wChannels;
+    Uint32 dwSamplesPerSec;
+    Uint32 dwAvgBytesPerSec;
+    Uint16 wBlockAlign;
+    Uint16 wBitsPerSample;
+    
+    void (*free)(struct S_FMT_T *fmt);
+    Uint32(*read_sample)(Sound_Sample *sample);
+
+    union
+    {
+        struct
+        {
+            Uint16 cbSize;
+            Uint16 wSamplesPerBlock;
+            Uint16 wNumCoef;
+            ADPCMCOEFSET *aCoeff;
+        } adpcm;
+    } fmt;
+} fmt_t;
+
+
 /*
  * Read in a fmt_t from disk. This makes this process safe regardless of
  *  the processor's byte order or how the fmt_t structure is packed.
+ * Note that the union "fmt" is not read in here; that is handled as 
+ *  needed in the read_fmt_* functions.
  */
 static int read_fmt_chunk(SDL_RWops *rw, fmt_t *fmt)
 {
@@ -153,6 +172,11 @@
 } /* read_fmt_chunk */
 
 
+
+/*****************************************************************************
+ * The DATA chunk...                                                         *
+ *****************************************************************************/
+
 #define dataID 0x61746164  /* "data", in ascii. */
 
 typedef struct
@@ -164,7 +188,7 @@
 
 
 /*
- * Read in a fmt_t from disk. This makes this process safe regardless of
+ * Read in a data_t from disk. This makes this process safe regardless of
  *  the processor's byte order or how the fmt_t structure is packed.
  */
 static int read_data_chunk(SDL_RWops *rw, data_t *data)
@@ -180,77 +204,26 @@
 } /* read_data_chunk */
 
 
-static int find_chunk(SDL_RWops *rw, Uint32 id)
-{
-    Sint32 siz = 0;
-    Uint32 _id = 0;
-
-    while (1)
-    {
-        BAIL_IF_MACRO(SDL_RWread(rw, &_id, sizeof (_id), 1) != 1, NULL, 0);
-        if (SDL_SwapLE32(_id) == id)
-            return(1);
-
-        BAIL_IF_MACRO(SDL_RWread(rw, &siz, sizeof (siz), 1) != 1, NULL, 0);
-        siz = SDL_SwapLE32(siz);
-        assert(siz > 0);
-        BAIL_IF_MACRO(SDL_RWseek(rw, siz, SEEK_SET) != siz, NULL, 0);
-    } /* while */
-
-    return(0);  /* shouldn't hit this, but just in case... */
-} /* find_chunk */
 
 
-static int WAV_open(Sound_Sample *sample, const char *ext)
-{
-    Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
-    SDL_RWops *rw = internal->rw;
-    fmt_t f;
-    data_t d;
-    wav_t *w;
-
-    BAIL_IF_MACRO(SDL_ReadLE32(rw) != riffID, "WAV: Not a RIFF file.", 0);
-	SDL_ReadLE32(rw);  /* throw the length away; we get this info later. */
-    BAIL_IF_MACRO(SDL_ReadLE32(rw) != waveID, "WAV: Not a WAVE file.", 0);
-    BAIL_IF_MACRO(!find_chunk(rw, fmtID), "WAV: No format chunk.", 0);
-    BAIL_IF_MACRO(!read_fmt_chunk(rw, &f), "WAV: Can't read format chunk.", 0);
-
-    /* !!! FIXME: This will have to change for compression types... */
-    BAIL_IF_MACRO(f.wFormatTag != FMT_NORMAL, "WAV: Unsupported encoding.", 0);
-
-    sample->actual.channels = (Uint8) f.wChannels;
-    sample->actual.rate = f.dwSamplesPerSec;
+/*****************************************************************************
+ * this is what we store in our internal->decoder_private field...           *
+ *****************************************************************************/
 
-    if (f.wBitsPerSample <= 8)
-        sample->actual.format = AUDIO_U8;
-    else if (f.wBitsPerSample <= 16)
-        sample->actual.format = AUDIO_S16LSB;
-    else
-        BAIL_MACRO("WAV: Unsupported sample size.", 0);
-
-    BAIL_IF_MACRO(!find_chunk(rw, dataID), "WAV: No data chunk.", 0);
-    BAIL_IF_MACRO(!read_data_chunk(rw, &d), "WAV: Can't read data chunk.", 0);
-
-    w = (wav_t *) malloc(sizeof(wav_t));
-    BAIL_IF_MACRO(w == NULL, ERR_OUT_OF_MEMORY, 0);
-    w->bytesLeft = d.chunkSize;
-    internal->decoder_private = (void *) w;
-
-    sample->flags = SOUND_SAMPLEFLAG_NONE;
-
-    SNDDBG(("WAV: Accepting data stream.\n"));
-    return(1); /* we'll handle this data. */
-} /* WAV_open */
+typedef struct
+{
+    fmt_t *fmt;
+    Sint32 bytesLeft;
+} wav_t;
 
 
-static void WAV_close(Sound_Sample *sample)
-{
-    Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
-    free(internal->decoder_private);
-} /* WAV_close */
 
 
-static Uint32 WAV_read(Sound_Sample *sample)
+/*****************************************************************************
+ * Normal, uncompressed waveform handler...                                  *
+ *****************************************************************************/
+
+static Uint32 read_sample_fmt_normal(Sound_Sample *sample)
 {
     Uint32 retval;
     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
@@ -280,6 +253,221 @@
         sample->flags |= SOUND_SAMPLEFLAG_EAGAIN;
 
     return(retval);
+} /* read_sample_fmt_normal */
+
+
+static void free_fmt_normal(fmt_t *fmt)
+{
+    /* it's a no-op. */
+} /* free_fmt_normal */
+
+
+static int read_fmt_normal(SDL_RWops *rw, fmt_t *fmt)
+{
+    fmt->free = free_fmt_normal;
+    fmt->read_sample = read_sample_fmt_normal;
+    return(1);
+} /* read_fmt_normal */
+
+
+
+/*****************************************************************************
+ * ADPCM compression handler...                                              *
+ *****************************************************************************/
+
+static Uint32 read_sample_fmt_adpcm(Sound_Sample *sample)
+{
+    /* !!! FIXME: Write this. */
+    sample->flags | SOUND_SAMPLEFLAG_ERROR;
+    return(0);
+} /* read_sample_fmt_adpcm */
+
+
+static void free_fmt_adpcm(fmt_t *fmt)
+{
+    if (fmt->fmt.adpcm.aCoeff != NULL)
+        free(fmt->fmt.adpcm.aCoeff);
+} /* free_fmt_adpcm */
+
+
+/*
+ * Read in a the adpcm-specific info from disk. This makes this process 
+ *  safe regardless of the processor's byte order or how the fmt_t 
+ *  structure is packed.
+ */
+static int read_fmt_adpcm(SDL_RWops *rw, fmt_t *fmt)
+{
+    size_t i;
+
+    fmt->fmt.adpcm.aCoeff = NULL;
+    fmt->free = free_fmt_adpcm;
+    fmt->read_sample = read_sample_fmt_adpcm;
+
+    if (SDL_RWread(rw, &fmt->fmt.adpcm.cbSize,
+                   sizeof (fmt->fmt.adpcm.cbSize), 1) != 1)
+    {
+        return(0);
+    } /* if */
+    fmt->fmt.adpcm.cbSize = SDL_SwapLE16(fmt->fmt.adpcm.cbSize);
+
+    if (SDL_RWread(rw, &fmt->fmt.adpcm.wSamplesPerBlock,
+                   sizeof (fmt->fmt.adpcm.wSamplesPerBlock), 1) != 1)
+    {
+        return(0);
+    } /* if */
+    fmt->fmt.adpcm.wSamplesPerBlock = SDL_SwapLE16(fmt->fmt.adpcm.wSamplesPerBlock);
+
+    if (SDL_RWread(rw, &fmt->fmt.adpcm.wNumCoef,
+                   sizeof (fmt->fmt.adpcm.wNumCoef), 1) != 1)
+    {
+        return(0);
+    } /* if */
+    fmt->fmt.adpcm.wNumCoef = SDL_SwapLE16(fmt->fmt.adpcm.wNumCoef);
+
+    i = sizeof (ADPCMCOEFSET) * fmt->fmt.adpcm.wNumCoef;
+    fmt->fmt.adpcm.aCoeff = (ADPCMCOEFSET *) malloc(i);
+    BAIL_IF_MACRO(fmt->fmt.adpcm.aCoeff == NULL, ERR_OUT_OF_MEMORY, 0);
+
+    for (i = 0; i < fmt->fmt.adpcm.wNumCoef; i++)
+    {
+        if (SDL_RWread(rw, &fmt->fmt.adpcm.aCoeff[i].iCoef1,
+                       sizeof (fmt->fmt.adpcm.aCoeff[i].iCoef1), 1) != 1)
+        {
+            return(0);
+        } /* if */
+        
+        if (SDL_RWread(rw, &fmt->fmt.adpcm.aCoeff[i].iCoef2,
+                       sizeof (fmt->fmt.adpcm.aCoeff[i].iCoef2), 1) != 1)
+        {
+            return(0);
+        } /* if */
+    } /* for */
+
+    return(1);
+} /* read_fmt_adpcm */
+
+
+/*****************************************************************************
+ * Everything else...                                                        *
+ *****************************************************************************/
+
+
+static int read_fmt(SDL_RWops *rw, fmt_t *fmt)
+{
+    /* if it's in this switch statement, we support the format. */
+    switch (fmt->wFormatTag)
+    {
+        case FMT_NORMAL:
+            return(read_fmt_normal(rw, fmt));
+
+        case FMT_ADPCM:
+            return(read_fmt_adpcm(rw, fmt));
+    } /* switch */
+
+    SNDDBG(("WAV: Format %d is unknown.\n", (int) fmt->wFormatTag));
+    Sound_SetError("WAV: Unsupported format");
+    return(0);  /* not supported whatsoever. */
+} /* read_fmt */
+
+
+/*
+ * Locate a specific chunk in the WAVE file by ID...
+ */
+static int find_chunk(SDL_RWops *rw, Uint32 id)
+{
+    Sint32 siz = 0;
+    Uint32 _id = 0;
+
+    while (1)
+    {
+        BAIL_IF_MACRO(SDL_RWread(rw, &_id, sizeof (_id), 1) != 1, NULL, 0);
+        if (SDL_SwapLE32(_id) == id)
+            return(1);
+
+        BAIL_IF_MACRO(SDL_RWread(rw, &siz, sizeof (siz), 1) != 1, NULL, 0);
+        siz = SDL_SwapLE32(siz);
+        assert(siz > 0);
+        BAIL_IF_MACRO(SDL_RWseek(rw, siz, SEEK_SET) != siz, NULL, 0);
+    } /* while */
+
+    return(0);  /* shouldn't hit this, but just in case... */
+} /* find_chunk */
+
+
+static int WAV_open_internal(Sound_Sample *sample, const char *ext, fmt_t *fmt)
+{
+    Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+    SDL_RWops *rw = internal->rw;
+    data_t d;
+    wav_t *w;
+
+    BAIL_IF_MACRO(SDL_ReadLE32(rw) != riffID, "WAV: Not a RIFF file.", 0);
+    SDL_ReadLE32(rw);  /* throw the length away; we get this info later. */
+    BAIL_IF_MACRO(SDL_ReadLE32(rw) != waveID, "WAV: Not a WAVE file.", 0);
+    BAIL_IF_MACRO(!find_chunk(rw, fmtID), "WAV: No format chunk.", 0);
+    BAIL_IF_MACRO(!read_fmt_chunk(rw, fmt), "WAV: Can't read format chunk.", 0);
+
+    sample->actual.channels = (Uint8) fmt->wChannels;
+    sample->actual.rate = fmt->dwSamplesPerSec;
+    if (fmt->wBitsPerSample <= 8)
+        sample->actual.format = AUDIO_U8;
+    else if (fmt->wBitsPerSample <= 16)
+        sample->actual.format = AUDIO_S16LSB;
+    else
+        BAIL_MACRO("WAV: Unsupported sample size.", 0);
+
+    BAIL_IF_MACRO(!read_fmt(rw, fmt), NULL, 0);
+    BAIL_IF_MACRO(!find_chunk(rw, dataID), "WAV: No data chunk.", 0);
+    BAIL_IF_MACRO(!read_data_chunk(rw, &d), "WAV: Can't read data chunk.", 0);
+
+    w = (wav_t *) malloc(sizeof(wav_t));
+    BAIL_IF_MACRO(w == NULL, ERR_OUT_OF_MEMORY, 0);
+    w->fmt = fmt;
+    w->bytesLeft = d.chunkSize;
+    internal->decoder_private = (void *) w;
+
+    sample->flags = SOUND_SAMPLEFLAG_NONE;
+
+    SNDDBG(("WAV: Accepting data stream.\n"));
+    return(1); /* we'll handle this data. */
+} /* WAV_open_internal */
+
+
+static int WAV_open(Sound_Sample *sample, const char *ext)
+{
+    int rc;
+
+    fmt_t *fmt = (fmt_t *) malloc(sizeof (fmt_t));
+    BAIL_IF_MACRO(fmt == NULL, ERR_OUT_OF_MEMORY, 0);
+    memset(fmt, '\0', sizeof (fmt_t));
+
+    rc = WAV_open_internal(sample, ext, fmt);
+    if (!rc)
+    {
+        if (fmt->free != NULL)
+            fmt->free(fmt);
+        free(fmt);
+    } /* if */
+
+    return(rc);
+} /* WAV_open */
+
+
+static void WAV_close(Sound_Sample *sample)
+{
+    Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+    wav_t *w = (wav_t *) internal->decoder_private;
+    w->fmt->free(w->fmt);
+    free(w->fmt);
+    free(w);
+} /* WAV_close */
+
+
+static Uint32 WAV_read(Sound_Sample *sample)
+{
+    Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+    wav_t *w = (wav_t *) internal->decoder_private;
+    return(w->fmt->read_sample(sample));
 } /* WAV_read */
 
 #endif /* SOUND_SUPPORTS_WAV */