wavestream.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 29 Jan 2016 12:44:13 -0800
changeset 718 fb0562cc1559
parent 711 f40c5ac95b12
child 725 bdf7b8d20566
permissions -rw-r--r--
Added Mix_OpenAudioDevice() so you can specify the audio device to open
slouken@0
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@711
     3
  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@518
     5
  This software is provided 'as-is', without any express or implied
slouken@518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@518
     7
  arising from the use of this software.
slouken@0
     8
slouken@518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@518
    10
  including commercial applications, and to alter it and redistribute it
slouken@518
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@518
    14
     claim that you wrote the original software. If you use this software
slouken@518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@518
    16
     appreciated but is not required.
slouken@518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@518
    18
     misrepresented as being the original software.
slouken@518
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@0
    21
slouken@140
    22
/* $Id$ */
slouken@138
    23
slouken@0
    24
/* This file supports streaming WAV files, without volume adjustment */
slouken@0
    25
slouken@0
    26
#include <stdlib.h>
slouken@0
    27
#include <string.h>
slouken@0
    28
slouken@24
    29
#include "SDL_audio.h"
slouken@24
    30
#include "SDL_mutex.h"
slouken@24
    31
#include "SDL_rwops.h"
slouken@24
    32
#include "SDL_endian.h"
slouken@0
    33
slouken@135
    34
#include "SDL_mixer.h"
slouken@0
    35
#include "wavestream.h"
slouken@0
    36
slouken@111
    37
/*
slouken@111
    38
    Taken with permission from SDL_wave.h, part of the SDL library,
slouken@111
    39
    available at: http://www.libsdl.org/
slouken@111
    40
    and placed under the same license as this mixer library.
slouken@111
    41
*/
slouken@111
    42
slouken@111
    43
/* WAVE files are little-endian */
slouken@111
    44
slouken@111
    45
/*******************************************/
slouken@111
    46
/* Define values for Microsoft WAVE format */
slouken@111
    47
/*******************************************/
slouken@617
    48
#define RIFF        0x46464952      /* "RIFF" */
slouken@617
    49
#define WAVE        0x45564157      /* "WAVE" */
slouken@684
    50
#define FMT         0x20746D66      /* "fmt " */
slouken@617
    51
#define DATA        0x61746164      /* "data" */
slouken@709
    52
#define SMPL        0x6c706d73      /* "smpl" */
slouken@617
    53
#define PCM_CODE    1
slouken@617
    54
#define ADPCM_CODE  2
slouken@617
    55
#define WAVE_MONO   1
slouken@617
    56
#define WAVE_STEREO 2
slouken@111
    57
slouken@709
    58
typedef struct {
slouken@111
    59
/* Not saved in the chunk we read:
slouken@709
    60
    Uint32  chunkID;
slouken@709
    61
    Uint32  chunkLen;
slouken@111
    62
*/
slouken@617
    63
    Uint16  encoding;
slouken@617
    64
    Uint16  channels;       /* 1 = mono, 2 = stereo */
slouken@617
    65
    Uint32  frequency;      /* One of 11025, 22050, or 44100 Hz */
slouken@617
    66
    Uint32  byterate;       /* Average bytes per second */
slouken@617
    67
    Uint16  blockalign;     /* Bytes per sample block */
slouken@617
    68
    Uint16  bitspersample;      /* One of 8, 12, 16, or 4 for ADPCM */
slouken@111
    69
} WaveFMT;
slouken@111
    70
slouken@709
    71
typedef struct {
slouken@709
    72
    Uint32 identifier;
slouken@709
    73
    Uint32 type;
slouken@709
    74
    Uint32 start;
slouken@709
    75
    Uint32 end;
slouken@709
    76
    Uint32 fraction;
slouken@709
    77
    Uint32 play_count;
slouken@709
    78
} SampleLoop;
slouken@709
    79
slouken@709
    80
typedef struct {
slouken@709
    81
/* Not saved in the chunk we read:
slouken@709
    82
    Uint32  chunkID;
slouken@709
    83
    Uint32  chunkLen;
slouken@709
    84
*/
slouken@709
    85
    Uint32  manufacturer;
slouken@709
    86
    Uint32  product;
slouken@709
    87
    Uint32  sample_period;
slouken@709
    88
    Uint32  MIDI_unity_note;
slouken@709
    89
    Uint32  MIDI_pitch_fraction;
slouken@709
    90
    Uint32  SMTPE_format;
slouken@709
    91
    Uint32  SMTPE_offset;
slouken@709
    92
    Uint32  sample_loops;
slouken@709
    93
    Uint32  sampler_data;
slouken@709
    94
    SampleLoop loops[];
slouken@709
    95
} SamplerChunk;
slouken@111
    96
slouken@111
    97
/*********************************************/
slouken@111
    98
/* Define values for AIFF (IFF audio) format */
slouken@111
    99
/*********************************************/
slouken@617
   100
#define FORM        0x4d524f46      /* "FORM" */
slouken@617
   101
#define AIFF        0x46464941      /* "AIFF" */
slouken@617
   102
#define SSND        0x444e5353      /* "SSND" */
slouken@617
   103
#define COMM        0x4d4d4f43      /* "COMM" */
slouken@111
   104
slouken@111
   105
slouken@64
   106
/* Currently we only support a single stream at a time */
slouken@173
   107
static WAVStream *music = NULL;
slouken@64
   108
slouken@0
   109
/* This is the format of the audio mixer data */
slouken@0
   110
static SDL_AudioSpec mixer;
slouken@214
   111
static int wavestream_volume = MIX_MAX_VOLUME;
slouken@0
   112
slouken@0
   113
/* Function to load the WAV/AIFF stream */
slouken@709
   114
static SDL_bool LoadWAVStream(WAVStream *wave);
slouken@709
   115
static SDL_bool LoadAIFFStream(WAVStream *wave);
slouken@0
   116
slouken@0
   117
/* Initialize the WAVStream player, with the given mixer settings
slouken@0
   118
   This function returns 0, or -1 if there was an error.
slouken@0
   119
 */
slouken@0
   120
int WAVStream_Init(SDL_AudioSpec *mixerfmt)
slouken@0
   121
{
slouken@617
   122
    mixer = *mixerfmt;
slouken@617
   123
    return(0);
slouken@0
   124
}
slouken@0
   125
slouken@214
   126
void WAVStream_SetVolume(int volume)
slouken@0
   127
{
slouken@617
   128
    wavestream_volume = volume;
slouken@0
   129
}
slouken@0
   130
slouken@542
   131
/* Load a WAV stream from the given RWops object */
slouken@625
   132
WAVStream *WAVStream_LoadSong_RW(SDL_RWops *src, int freesrc)
slouken@0
   133
{
slouken@617
   134
    WAVStream *wave;
slouken@709
   135
    SDL_bool loaded = SDL_FALSE;
slouken@0
   136
slouken@709
   137
    if (!mixer.format) {
slouken@617
   138
        Mix_SetError("WAV music output not started");
slouken@617
   139
        return(NULL);
slouken@617
   140
    }
slouken@709
   141
slouken@617
   142
    wave = (WAVStream *)SDL_malloc(sizeof *wave);
slouken@709
   143
    if (wave) {
slouken@625
   144
        Uint32 magic;
slouken@625
   145
slouken@625
   146
        SDL_zerop(wave);
slouken@709
   147
        wave->src = src;
slouken@625
   148
        wave->freesrc = freesrc;
slouken@625
   149
slouken@625
   150
        magic = SDL_ReadLE32(src);
slouken@709
   151
        if (magic == RIFF || magic == WAVE) {
slouken@709
   152
            loaded = LoadWAVStream(wave);
slouken@709
   153
        } else if (magic == FORM) {
slouken@709
   154
            loaded = LoadAIFFStream(wave);
slouken@617
   155
        } else {
slouken@617
   156
            Mix_SetError("Unknown WAVE format");
slouken@617
   157
        }
slouken@709
   158
        if (!loaded) {
slouken@709
   159
            WAVStream_FreeSong(wave);
slouken@617
   160
            return(NULL);
slouken@617
   161
        }
slouken@617
   162
        SDL_BuildAudioCVT(&wave->cvt,
slouken@709
   163
            wave->spec.format, wave->spec.channels, wave->spec.freq,
slouken@617
   164
            mixer.format, mixer.channels, mixer.freq);
slouken@617
   165
    } else {
slouken@617
   166
        SDL_OutOfMemory();
slouken@617
   167
        return(NULL);
slouken@617
   168
    }
slouken@617
   169
    return(wave);
slouken@0
   170
}
slouken@0
   171
slouken@0
   172
/* Start playback of a given WAV stream */
slouken@214
   173
void WAVStream_Start(WAVStream *wave)
slouken@0
   174
{
slouken@709
   175
    int i;
slouken@709
   176
    for (i = 0; i < wave->numloops; ++i) {
slouken@709
   177
        WAVLoopPoint *loop = &wave->loops[i];
slouken@709
   178
        loop->active = SDL_TRUE;
slouken@709
   179
        loop->current_play_count = loop->initial_play_count;
slouken@709
   180
    }
slouken@709
   181
    SDL_RWseek(wave->src, wave->start, RW_SEEK_SET);
slouken@617
   182
    music = wave;
slouken@0
   183
}
slouken@0
   184
slouken@173
   185
/* Play some of a stream previously started with WAVStream_Start() */
slouken@709
   186
static int PlaySome(Uint8 *stream, int len)
slouken@709
   187
{
slouken@709
   188
    Sint64 pos, stop;
slouken@709
   189
    WAVLoopPoint *loop;
slouken@709
   190
    Sint64 loop_start;
slouken@709
   191
    Sint64 loop_stop;
slouken@709
   192
    int i;
slouken@709
   193
    int consumed;
slouken@709
   194
slouken@709
   195
    pos = SDL_RWtell(music->src);
slouken@709
   196
    stop = music->stop;
slouken@709
   197
    loop = NULL;
slouken@709
   198
    for (i = 0; i < music->numloops; ++i) {
slouken@709
   199
        loop = &music->loops[i];
slouken@709
   200
        if (loop->active) {
slouken@709
   201
            const int bytes_per_sample = (SDL_AUDIO_BITSIZE(music->spec.format) / 8) * music->spec.channels;
slouken@709
   202
            loop_start = music->start + loop->start * bytes_per_sample;
slouken@709
   203
            loop_stop = music->start + (loop->stop + 1) * bytes_per_sample;
slouken@709
   204
            if (pos >= loop_start && pos < loop_stop)
slouken@709
   205
            {
slouken@709
   206
                stop = loop_stop;
slouken@709
   207
                break;
slouken@709
   208
            }
slouken@709
   209
        }
slouken@709
   210
        loop = NULL;
slouken@709
   211
    }
slouken@709
   212
slouken@709
   213
    if (music->cvt.needed) {
slouken@709
   214
        int original_len;
slouken@709
   215
slouken@709
   216
        original_len = (int)((double)len/music->cvt.len_ratio);
slouken@709
   217
        if (music->cvt.len != original_len) {
slouken@709
   218
            int worksize;
slouken@709
   219
            if (music->cvt.buf != NULL) {
slouken@709
   220
                SDL_free(music->cvt.buf);
slouken@709
   221
            }
slouken@709
   222
            worksize = original_len*music->cvt.len_mult;
slouken@709
   223
            music->cvt.buf=(Uint8 *)SDL_malloc(worksize);
slouken@709
   224
            if (music->cvt.buf == NULL) {
slouken@709
   225
                return 0;
slouken@709
   226
            }
slouken@709
   227
            music->cvt.len = original_len;
slouken@709
   228
        }
slouken@709
   229
        if ((stop - pos) < original_len) {
slouken@709
   230
            original_len = (int)(stop - pos);
slouken@709
   231
        }
slouken@709
   232
        original_len = SDL_RWread(music->src, music->cvt.buf, 1, original_len);
slouken@709
   233
        /* At least at the time of writing, SDL_ConvertAudio()
slouken@709
   234
           does byte-order swapping starting at the end of the
slouken@709
   235
           buffer. Thus, if we are reading 16-bit samples, we
slouken@709
   236
           had better make damn sure that we get an even
slouken@709
   237
           number of bytes, or we'll get garbage.
slouken@709
   238
         */
slouken@709
   239
        if ((music->cvt.src_format & 0x0010) && (original_len & 1)) {
slouken@709
   240
            original_len--;
slouken@709
   241
        }
slouken@709
   242
        music->cvt.len = original_len;
slouken@709
   243
        SDL_ConvertAudio(&music->cvt);
slouken@718
   244
        SDL_MixAudioFormat(stream, music->cvt.buf, mixer.format,music->cvt.len_cvt, wavestream_volume);
slouken@709
   245
        consumed = music->cvt.len_cvt;
slouken@709
   246
    } else {
slouken@709
   247
        Uint8 *data;
slouken@709
   248
        if ((stop - pos) < len) {
slouken@709
   249
            len = (int)(stop - pos);
slouken@709
   250
        }
slouken@709
   251
        data = SDL_stack_alloc(Uint8, len);
slouken@709
   252
        if (data) {
slouken@709
   253
            len = SDL_RWread(music->src, data, 1, len);
slouken@718
   254
            SDL_MixAudioFormat(stream, data, mixer.format, len, wavestream_volume);
slouken@709
   255
            SDL_stack_free(data);
slouken@709
   256
        }
slouken@709
   257
        consumed = len;
slouken@709
   258
    }
slouken@709
   259
slouken@709
   260
    if (loop && SDL_RWtell(music->src) >= stop) {
slouken@709
   261
        if (loop->current_play_count == 1) {
slouken@709
   262
            loop->active = SDL_FALSE;
slouken@709
   263
        } else {
slouken@709
   264
            if (loop->current_play_count > 0) {
slouken@709
   265
                --loop->current_play_count;
slouken@709
   266
            }
slouken@709
   267
            SDL_RWseek(music->src, loop_start, RW_SEEK_SET);
slouken@709
   268
        }
slouken@709
   269
    }
slouken@709
   270
    return consumed;
slouken@709
   271
}
slouken@709
   272
slouken@407
   273
int WAVStream_PlaySome(Uint8 *stream, int len)
slouken@0
   274
{
slouken@709
   275
    if (!music)
slouken@709
   276
        return 0;
slouken@0
   277
slouken@709
   278
    while ((SDL_RWtell(music->src) < music->stop) && (len > 0)) {
slouken@709
   279
        int consumed = PlaySome(stream, len);
slouken@709
   280
        if (!consumed)
slouken@709
   281
            break;
slouken@0
   282
slouken@709
   283
        stream += consumed;
slouken@709
   284
        len -= consumed;
slouken@617
   285
    }
slouken@709
   286
    return len;
slouken@0
   287
}
slouken@0
   288
slouken@0
   289
/* Stop playback of a stream previously started with WAVStream_Start() */
slouken@214
   290
void WAVStream_Stop(void)
slouken@0
   291
{
slouken@617
   292
    music = NULL;
slouken@0
   293
}
slouken@0
   294
slouken@0
   295
/* Close the given WAV stream */
slouken@214
   296
void WAVStream_FreeSong(WAVStream *wave)
slouken@0
   297
{
slouken@709
   298
    if (wave) {
slouken@617
   299
        /* Clean up associated data */
slouken@709
   300
        if (wave->loops) {
slouken@709
   301
            SDL_free(wave->loops);
slouken@709
   302
        }
slouken@709
   303
        if (wave->cvt.buf) {
slouken@617
   304
            SDL_free(wave->cvt.buf);
slouken@617
   305
        }
slouken@709
   306
        if (wave->freesrc) {
slouken@625
   307
            SDL_RWclose(wave->src);
slouken@617
   308
        }
slouken@617
   309
        SDL_free(wave);
slouken@617
   310
    }
slouken@0
   311
}
slouken@0
   312
slouken@0
   313
/* Return non-zero if a stream is currently playing */
slouken@214
   314
int WAVStream_Active(void)
slouken@0
   315
{
slouken@617
   316
    int active;
slouken@0
   317
slouken@617
   318
    active = 0;
slouken@709
   319
    if (music && (SDL_RWtell(music->src) < music->stop)) {
slouken@617
   320
        active = 1;
slouken@617
   321
    }
slouken@617
   322
    return(active);
slouken@0
   323
}
slouken@0
   324
slouken@709
   325
static SDL_bool ParseFMT(WAVStream *wave, Uint32 chunk_length)
slouken@0
   326
{
slouken@709
   327
    SDL_RWops *src = wave->src;
slouken@709
   328
    SDL_AudioSpec *spec = &wave->spec;
slouken@709
   329
    WaveFMT *format;
slouken@709
   330
    Uint8 *data;
slouken@709
   331
    SDL_bool loaded = SDL_FALSE;
slouken@709
   332
slouken@709
   333
    if (chunk_length < sizeof(*format)) {
slouken@709
   334
        Mix_SetError("Wave format chunk too small");
slouken@709
   335
        return SDL_FALSE;
slouken@617
   336
    }
slouken@0
   337
slouken@709
   338
    data = (Uint8 *)SDL_malloc(chunk_length);
slouken@709
   339
    if (!data) {
slouken@709
   340
        Mix_SetError("Out of memory");
slouken@709
   341
        return SDL_FALSE;
slouken@709
   342
    }
slouken@709
   343
    if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
slouken@709
   344
        Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
slouken@709
   345
        return SDL_FALSE;
slouken@709
   346
    }
slouken@709
   347
    format = (WaveFMT *)data;
slouken@0
   348
slouken@617
   349
    /* Decode the audio data format */
slouken@617
   350
    switch (SDL_SwapLE16(format->encoding)) {
slouken@617
   351
        case PCM_CODE:
slouken@617
   352
            /* We can understand this */
slouken@617
   353
            break;
slouken@617
   354
        default:
slouken@617
   355
            Mix_SetError("Unknown WAVE data format");
slouken@617
   356
            goto done;
slouken@617
   357
    }
slouken@617
   358
    spec->freq = SDL_SwapLE32(format->frequency);
slouken@617
   359
    switch (SDL_SwapLE16(format->bitspersample)) {
slouken@617
   360
        case 8:
slouken@617
   361
            spec->format = AUDIO_U8;
slouken@617
   362
            break;
slouken@617
   363
        case 16:
slouken@617
   364
            spec->format = AUDIO_S16;
slouken@617
   365
            break;
slouken@617
   366
        default:
slouken@617
   367
            Mix_SetError("Unknown PCM data format");
slouken@617
   368
            goto done;
slouken@617
   369
    }
slouken@617
   370
    spec->channels = (Uint8) SDL_SwapLE16(format->channels);
slouken@617
   371
    spec->samples = 4096;       /* Good default buffer size */
slouken@0
   372
slouken@709
   373
    loaded = SDL_TRUE;
slouken@0
   374
slouken@0
   375
done:
slouken@709
   376
    SDL_free(data);
slouken@709
   377
    return loaded;
slouken@709
   378
}
slouken@709
   379
slouken@709
   380
static SDL_bool ParseDATA(WAVStream *wave, Uint32 chunk_length)
slouken@709
   381
{
slouken@709
   382
    wave->start = SDL_RWtell(wave->src);
slouken@709
   383
    wave->stop = wave->start + chunk_length;
slouken@709
   384
    SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR);
slouken@709
   385
    return SDL_TRUE;
slouken@709
   386
}
slouken@709
   387
slouken@709
   388
static SDL_bool AddLoopPoint(WAVStream *wave, Uint32 play_count, Uint32 start, Uint32 stop)
slouken@709
   389
{
slouken@709
   390
    WAVLoopPoint *loop;
slouken@709
   391
    WAVLoopPoint *loops = SDL_realloc(wave->loops, (wave->numloops + 1)*sizeof(*wave->loops));
slouken@709
   392
    if (!loops) {
slouken@709
   393
        Mix_SetError("Out of memory");
slouken@709
   394
        return SDL_FALSE;
slouken@617
   395
    }
slouken@709
   396
slouken@709
   397
    loop = &loops[ wave->numloops ];
slouken@709
   398
    loop->start = start;
slouken@709
   399
    loop->stop = stop;
slouken@709
   400
    loop->initial_play_count = play_count;
slouken@709
   401
    loop->current_play_count = play_count;
slouken@709
   402
slouken@709
   403
    wave->loops = loops;
slouken@709
   404
    ++wave->numloops;
slouken@709
   405
    return SDL_TRUE;
slouken@709
   406
}
slouken@709
   407
slouken@709
   408
static SDL_bool ParseSMPL(WAVStream *wave, Uint32 chunk_length)
slouken@709
   409
{
slouken@709
   410
    SamplerChunk *chunk;
slouken@709
   411
    Uint8 *data;
slouken@709
   412
    int i;
slouken@709
   413
    SDL_bool loaded = SDL_FALSE;
slouken@709
   414
slouken@709
   415
    data = (Uint8 *)SDL_malloc(chunk_length);
slouken@709
   416
    if (!data) {
slouken@709
   417
        Mix_SetError("Out of memory");
slouken@709
   418
        return SDL_FALSE;
slouken@617
   419
    }
slouken@709
   420
    if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
slouken@709
   421
        Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
slouken@709
   422
        return SDL_FALSE;
slouken@709
   423
    }
slouken@709
   424
    chunk = (SamplerChunk *)data;
slouken@709
   425
slouken@709
   426
    for (i = 0; i < SDL_SwapLE32(chunk->sample_loops); ++i) {
slouken@709
   427
        const Uint32 LOOP_TYPE_FORWARD = 0;
slouken@709
   428
        Uint32 loop_type = SDL_SwapLE32(chunk->loops[i].type);
slouken@709
   429
        if (loop_type == LOOP_TYPE_FORWARD) {
slouken@709
   430
            AddLoopPoint(wave, SDL_SwapLE32(chunk->loops[i].play_count), SDL_SwapLE32(chunk->loops[i].start), SDL_SwapLE32(chunk->loops[i].end));
slouken@709
   431
        }
slouken@709
   432
    }
slouken@709
   433
slouken@709
   434
    loaded = SDL_TRUE;
slouken@709
   435
slouken@709
   436
done:
slouken@709
   437
    SDL_free(data);
slouken@709
   438
    return loaded;
slouken@709
   439
}
slouken@709
   440
slouken@709
   441
static SDL_bool LoadWAVStream(WAVStream *wave)
slouken@709
   442
{
slouken@709
   443
    SDL_RWops *src = wave->src;
slouken@709
   444
    Uint32 chunk_type;
slouken@709
   445
    Uint32 chunk_length;
slouken@709
   446
    SDL_bool found_FMT = SDL_FALSE;
slouken@709
   447
    SDL_bool found_DATA = SDL_FALSE;
slouken@709
   448
slouken@709
   449
    /* WAV magic header */
slouken@709
   450
    Uint32 wavelen;
slouken@709
   451
    Uint32 WAVEmagic;
slouken@709
   452
slouken@709
   453
    /* Check the magic header */
slouken@709
   454
    wavelen = SDL_ReadLE32(src);
slouken@709
   455
    WAVEmagic = SDL_ReadLE32(src);
slouken@709
   456
slouken@709
   457
    /* Read the chunks */
slouken@709
   458
    for (; ;) {
slouken@709
   459
        chunk_type = SDL_ReadLE32(src);
slouken@709
   460
        chunk_length = SDL_ReadLE32(src);
slouken@709
   461
slouken@709
   462
        if (chunk_length == 0)
slouken@709
   463
            break;
slouken@709
   464
slouken@709
   465
        switch (chunk_type)
slouken@709
   466
        {
slouken@709
   467
        case FMT:
slouken@709
   468
            found_FMT = SDL_TRUE;
slouken@709
   469
            if (!ParseFMT(wave, chunk_length))
slouken@709
   470
                return SDL_FALSE;
slouken@709
   471
            break;
slouken@709
   472
        case DATA:
slouken@709
   473
            found_DATA = SDL_TRUE;
slouken@709
   474
            if (!ParseDATA(wave, chunk_length))
slouken@709
   475
                return SDL_FALSE;
slouken@709
   476
            break;
slouken@709
   477
        case SMPL:
slouken@709
   478
            if (!ParseSMPL(wave, chunk_length))
slouken@709
   479
                return SDL_FALSE;
slouken@709
   480
            break;
slouken@709
   481
        default:
slouken@709
   482
            SDL_RWseek(src, chunk_length, RW_SEEK_CUR);
slouken@709
   483
            break;
slouken@709
   484
        }
slouken@709
   485
    }
slouken@709
   486
slouken@709
   487
    if (!found_FMT) {
slouken@709
   488
        Mix_SetError("Bad WAV file (no FMT chunk)");
slouken@709
   489
        return SDL_FALSE;
slouken@709
   490
    }
slouken@709
   491
slouken@709
   492
    if (!found_DATA) {
slouken@709
   493
        Mix_SetError("Bad WAV file (no DATA chunk)");
slouken@709
   494
        return SDL_FALSE;
slouken@709
   495
    }
slouken@709
   496
slouken@709
   497
    return SDL_TRUE;
slouken@0
   498
}
slouken@0
   499
slouken@95
   500
/* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
slouken@95
   501
 * I don't pretend to fully understand it.
slouken@95
   502
 */
slouken@95
   503
slouken@95
   504
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
slouken@0
   505
{
slouken@617
   506
    /* Negative number? */
slouken@617
   507
    if (sanebuf[0] & 0x80)
slouken@617
   508
        return 0;
slouken@0
   509
slouken@617
   510
    /* Less than 1? */
slouken@617
   511
    if (sanebuf[0] <= 0x3F)
slouken@617
   512
        return 1;
slouken@95
   513
slouken@617
   514
    /* Way too big? */
slouken@617
   515
    if (sanebuf[0] > 0x40)
slouken@617
   516
        return 0x4000000;
slouken@95
   517
slouken@617
   518
    /* Still too big? */
slouken@617
   519
    if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
slouken@617
   520
        return 800000000;
slouken@95
   521
slouken@709
   522
    return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) |
slouken@709
   523
            (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
slouken@0
   524
}
slouken@0
   525
slouken@709
   526
static SDL_bool LoadAIFFStream(WAVStream *wave)
slouken@0
   527
{
slouken@709
   528
    SDL_RWops *src = wave->src;
slouken@709
   529
    SDL_AudioSpec *spec = &wave->spec;
slouken@709
   530
    SDL_bool found_SSND = SDL_FALSE;
slouken@709
   531
    SDL_bool found_COMM = SDL_FALSE;
slouken@0
   532
slouken@617
   533
    Uint32 chunk_type;
slouken@617
   534
    Uint32 chunk_length;
slouken@621
   535
    Sint64 next_chunk;
slouken@95
   536
slouken@617
   537
    /* AIFF magic header */
slouken@617
   538
    Uint32 AIFFmagic;
slouken@617
   539
    /* SSND chunk        */
slouken@617
   540
    Uint32 offset;
slouken@617
   541
    Uint32 blocksize;
slouken@617
   542
    /* COMM format chunk */
slouken@617
   543
    Uint16 channels = 0;
slouken@617
   544
    Uint32 numsamples = 0;
slouken@617
   545
    Uint16 samplesize = 0;
slouken@617
   546
    Uint8 sane_freq[10];
slouken@617
   547
    Uint32 frequency = 0;
slouken@0
   548
slouken@617
   549
    /* Check the magic header */
slouken@709
   550
    chunk_length = SDL_ReadBE32(src);
slouken@709
   551
    AIFFmagic = SDL_ReadLE32(src);
slouken@709
   552
    if (AIFFmagic != AIFF) {
slouken@617
   553
        Mix_SetError("Unrecognized file type (not AIFF)");
slouken@709
   554
        return SDL_FALSE;
slouken@617
   555
    }
slouken@0
   556
slouken@617
   557
    /* From what I understand of the specification, chunks may appear in
slouken@709
   558
     * any order, and we should just ignore unknown ones.
slouken@617
   559
     *
slouken@617
   560
     * TODO: Better sanity-checking. E.g. what happens if the AIFF file
slouken@617
   561
     *       contains compressed sound data?
slouken@625
   562
     */
slouken@617
   563
    do {
slouken@617
   564
        chunk_type      = SDL_ReadLE32(src);
slouken@617
   565
        chunk_length    = SDL_ReadBE32(src);
slouken@617
   566
        next_chunk      = SDL_RWtell(src) + chunk_length;
slouken@95
   567
slouken@617
   568
        /* Paranoia to avoid infinite loops */
slouken@617
   569
        if (chunk_length == 0)
slouken@709
   570
            break;
slouken@95
   571
slouken@625
   572
        switch (chunk_type) {
slouken@617
   573
        case SSND:
slouken@709
   574
            found_SSND = SDL_TRUE;
slouken@709
   575
            offset = SDL_ReadBE32(src);
slouken@709
   576
            blocksize = SDL_ReadBE32(src);
slouken@709
   577
            wave->start = SDL_RWtell(src) + offset;
slouken@617
   578
            break;
slouken@95
   579
slouken@617
   580
        case COMM:
slouken@709
   581
            found_COMM = SDL_TRUE;
slouken@95
   582
slouken@617
   583
            /* Read the audio data format chunk */
slouken@709
   584
            channels = SDL_ReadBE16(src);
slouken@709
   585
            numsamples = SDL_ReadBE32(src);
slouken@709
   586
            samplesize = SDL_ReadBE16(src);
slouken@617
   587
            SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
slouken@709
   588
            frequency = SANE_to_Uint32(sane_freq);
slouken@617
   589
            break;
slouken@95
   590
slouken@617
   591
        default:
slouken@617
   592
            break;
slouken@617
   593
        }
slouken@617
   594
    } while ((!found_SSND || !found_COMM)
slouken@617
   595
         && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
slouken@95
   596
slouken@617
   597
    if (!found_SSND) {
slouken@617
   598
        Mix_SetError("Bad AIFF file (no SSND chunk)");
slouken@709
   599
        return SDL_FALSE;
slouken@617
   600
    }
slouken@0
   601
slouken@617
   602
    if (!found_COMM) {
slouken@617
   603
        Mix_SetError("Bad AIFF file (no COMM chunk)");
slouken@709
   604
        return SDL_FALSE;
slouken@617
   605
    }
slouken@0
   606
slouken@709
   607
    wave->stop = wave->start + channels * numsamples * (samplesize / 8);
slouken@617
   608
slouken@617
   609
    /* Decode the audio data format */
slouken@621
   610
    SDL_memset(spec, 0, (sizeof *spec));
slouken@617
   611
    spec->freq = frequency;
slouken@617
   612
    switch (samplesize) {
slouken@617
   613
        case 8:
slouken@617
   614
            spec->format = AUDIO_S8;
slouken@617
   615
            break;
slouken@617
   616
        case 16:
slouken@617
   617
            spec->format = AUDIO_S16MSB;
slouken@617
   618
            break;
slouken@617
   619
        default:
slouken@617
   620
            Mix_SetError("Unknown samplesize in data format");
slouken@709
   621
            return SDL_FALSE;
slouken@617
   622
    }
slouken@617
   623
    spec->channels = (Uint8) channels;
slouken@617
   624
    spec->samples = 4096;       /* Good default buffer size */
slouken@0
   625
slouken@709
   626
    return SDL_TRUE;
slouken@0
   627
}
slouken@308
   628