music_wav.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 20 Oct 2017 23:39:04 -0700
changeset 797 b4b6adff699a
parent 779 a2b494c054d5
child 800 cc4aef7fef64
permissions -rw-r--r--
Switched to using SDL_AudioStream in SDL 2.0.7 for better streaming resampling support
Also made the following changes:
* Moved looping support into the music interfaces for better seamless looping when resampling
* Music interfaces always start play from the beginning of the song
* Implemented 24-bit and surround sound support for FLAC audio files
* Implemented sample dithering in MAD music interface using GPL code
* Improved error checking in music interfaces
slouken@0
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@725
     3
  Copyright (C) 1997-2017 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@777
    22
#ifdef MUSIC_WAV
slouken@138
    23
slouken@777
    24
/* This file supports streaming WAV files */
slouken@0
    25
slouken@777
    26
#include "music_wav.h"
slouken@0
    27
slouken@0
    28
slouken@777
    29
typedef struct {
slouken@777
    30
    SDL_bool active;
slouken@777
    31
    Uint32 start;
slouken@777
    32
    Uint32 stop;
slouken@777
    33
    Uint32 initial_play_count;
slouken@777
    34
    Uint32 current_play_count;
slouken@777
    35
} WAVLoopPoint;
slouken@777
    36
slouken@777
    37
typedef struct {
slouken@777
    38
    SDL_RWops *src;
slouken@777
    39
    SDL_bool freesrc;
slouken@777
    40
    SDL_AudioSpec spec;
slouken@777
    41
    int volume;
slouken@797
    42
    int play_count;
slouken@777
    43
    Sint64 start;
slouken@777
    44
    Sint64 stop;
slouken@797
    45
    Uint8 *buffer;
slouken@797
    46
    SDL_AudioStream *stream;
slouken@777
    47
    int numloops;
slouken@777
    48
    WAVLoopPoint *loops;
slouken@797
    49
} WAV_Music;
slouken@0
    50
slouken@111
    51
/*
slouken@111
    52
    Taken with permission from SDL_wave.h, part of the SDL library,
slouken@111
    53
    available at: http://www.libsdl.org/
slouken@111
    54
    and placed under the same license as this mixer library.
slouken@111
    55
*/
slouken@111
    56
slouken@111
    57
/* WAVE files are little-endian */
slouken@111
    58
slouken@111
    59
/*******************************************/
slouken@111
    60
/* Define values for Microsoft WAVE format */
slouken@111
    61
/*******************************************/
slouken@617
    62
#define RIFF        0x46464952      /* "RIFF" */
slouken@617
    63
#define WAVE        0x45564157      /* "WAVE" */
slouken@684
    64
#define FMT         0x20746D66      /* "fmt " */
slouken@617
    65
#define DATA        0x61746164      /* "data" */
slouken@709
    66
#define SMPL        0x6c706d73      /* "smpl" */
slouken@617
    67
#define PCM_CODE    1
slouken@617
    68
#define ADPCM_CODE  2
slouken@617
    69
#define WAVE_MONO   1
slouken@617
    70
#define WAVE_STEREO 2
slouken@111
    71
slouken@709
    72
typedef struct {
slouken@111
    73
/* Not saved in the chunk we read:
slouken@709
    74
    Uint32  chunkID;
slouken@709
    75
    Uint32  chunkLen;
slouken@111
    76
*/
slouken@617
    77
    Uint16  encoding;
slouken@617
    78
    Uint16  channels;       /* 1 = mono, 2 = stereo */
slouken@617
    79
    Uint32  frequency;      /* One of 11025, 22050, or 44100 Hz */
slouken@617
    80
    Uint32  byterate;       /* Average bytes per second */
slouken@617
    81
    Uint16  blockalign;     /* Bytes per sample block */
slouken@617
    82
    Uint16  bitspersample;      /* One of 8, 12, 16, or 4 for ADPCM */
slouken@111
    83
} WaveFMT;
slouken@111
    84
slouken@709
    85
typedef struct {
slouken@709
    86
    Uint32 identifier;
slouken@709
    87
    Uint32 type;
slouken@709
    88
    Uint32 start;
slouken@709
    89
    Uint32 end;
slouken@709
    90
    Uint32 fraction;
slouken@709
    91
    Uint32 play_count;
slouken@709
    92
} SampleLoop;
slouken@709
    93
slouken@709
    94
typedef struct {
slouken@709
    95
/* Not saved in the chunk we read:
slouken@709
    96
    Uint32  chunkID;
slouken@709
    97
    Uint32  chunkLen;
slouken@709
    98
*/
slouken@709
    99
    Uint32  manufacturer;
slouken@709
   100
    Uint32  product;
slouken@709
   101
    Uint32  sample_period;
slouken@709
   102
    Uint32  MIDI_unity_note;
slouken@709
   103
    Uint32  MIDI_pitch_fraction;
slouken@709
   104
    Uint32  SMTPE_format;
slouken@709
   105
    Uint32  SMTPE_offset;
slouken@709
   106
    Uint32  sample_loops;
slouken@709
   107
    Uint32  sampler_data;
slouken@709
   108
    SampleLoop loops[];
slouken@709
   109
} SamplerChunk;
slouken@111
   110
slouken@111
   111
/*********************************************/
slouken@111
   112
/* Define values for AIFF (IFF audio) format */
slouken@111
   113
/*********************************************/
slouken@617
   114
#define FORM        0x4d524f46      /* "FORM" */
slouken@617
   115
#define AIFF        0x46464941      /* "AIFF" */
slouken@617
   116
#define SSND        0x444e5353      /* "SSND" */
slouken@617
   117
#define COMM        0x4d4d4f43      /* "COMM" */
slouken@111
   118
slouken@111
   119
slouken@0
   120
/* Function to load the WAV/AIFF stream */
slouken@797
   121
static SDL_bool LoadWAVMusic(WAV_Music *wave);
slouken@797
   122
static SDL_bool LoadAIFFMusic(WAV_Music *wave);
slouken@0
   123
slouken@797
   124
static void WAV_Delete(void *context);
slouken@0
   125
slouken@542
   126
/* Load a WAV stream from the given RWops object */
slouken@797
   127
static void *WAV_CreateFromRW(SDL_RWops *src, int freesrc)
slouken@0
   128
{
slouken@797
   129
    WAV_Music *music;
slouken@797
   130
    Uint32 magic;
slouken@709
   131
    SDL_bool loaded = SDL_FALSE;
slouken@0
   132
slouken@797
   133
    music = (WAV_Music *)SDL_calloc(1, sizeof(*music));
slouken@797
   134
    if (!music) {
slouken@797
   135
        SDL_OutOfMemory();
slouken@797
   136
        return NULL;
slouken@797
   137
    }
slouken@797
   138
    music->volume = MIX_MAX_VOLUME;
slouken@625
   139
slouken@797
   140
    magic = SDL_ReadLE32(src);
slouken@797
   141
    if (magic == RIFF || magic == WAVE) {
slouken@797
   142
        loaded = LoadWAVMusic(music);
slouken@797
   143
    } else if (magic == FORM) {
slouken@797
   144
        loaded = LoadAIFFMusic(music);
slouken@797
   145
    } else {
slouken@797
   146
        Mix_SetError("Unknown WAVE format");
slouken@797
   147
    }
slouken@797
   148
    if (!loaded) {
slouken@797
   149
        SDL_free(music);
slouken@797
   150
        return NULL;
slouken@797
   151
    }
slouken@797
   152
    music->buffer = (Uint8*)SDL_malloc(music->spec.size);
slouken@797
   153
    if (!music->buffer) {
slouken@797
   154
        WAV_Delete(music);
slouken@797
   155
        return NULL;
slouken@797
   156
    }
slouken@797
   157
    music->stream = SDL_NewAudioStream(
slouken@797
   158
        music->spec.format, music->spec.channels, music->spec.freq,
slouken@797
   159
        music_spec.format, music_spec.channels, music_spec.freq);
slouken@797
   160
    if (!music->stream) {
slouken@797
   161
        WAV_Delete(music);
slouken@797
   162
        return NULL;
slouken@797
   163
    }
slouken@625
   164
slouken@797
   165
    music->src = src;
slouken@797
   166
    music->freesrc = freesrc;
slouken@797
   167
    return music;
slouken@777
   168
}
slouken@777
   169
slouken@797
   170
static void WAV_SetVolume(void *context, int volume)
slouken@777
   171
{
slouken@797
   172
    WAV_Music *music = (WAV_Music *)context;
slouken@797
   173
    music->volume = volume;
slouken@0
   174
}
slouken@0
   175
slouken@0
   176
/* Start playback of a given WAV stream */
slouken@797
   177
static int WAV_Play(void *context, int play_count)
slouken@0
   178
{
slouken@797
   179
    WAV_Music *music = (WAV_Music *)context;
slouken@709
   180
    int i;
slouken@797
   181
    for (i = 0; i < music->numloops; ++i) {
slouken@797
   182
        WAVLoopPoint *loop = &music->loops[i];
slouken@709
   183
        loop->active = SDL_TRUE;
slouken@709
   184
        loop->current_play_count = loop->initial_play_count;
slouken@709
   185
    }
slouken@797
   186
    music->play_count = play_count;
slouken@797
   187
    return SDL_RWseek(music->src, music->start, RW_SEEK_SET);
slouken@0
   188
}
slouken@0
   189
slouken@797
   190
/* Play some of a stream previously started with WAV_Play() */
slouken@797
   191
static int WAV_GetSome(void *context, void *data, int bytes, SDL_bool *done)
slouken@709
   192
{
slouken@797
   193
    WAV_Music *music = (WAV_Music *)context;
slouken@709
   194
    Sint64 pos, stop;
slouken@709
   195
    WAVLoopPoint *loop;
slouken@709
   196
    Sint64 loop_start;
slouken@709
   197
    Sint64 loop_stop;
slouken@797
   198
    SDL_bool looped = SDL_FALSE;
slouken@709
   199
    int i;
slouken@797
   200
    int filled, amount, result;
slouken@709
   201
slouken@797
   202
    filled = SDL_AudioStreamGet(music->stream, data, bytes);
slouken@797
   203
    if (filled != 0) {
slouken@797
   204
        return filled;
slouken@797
   205
    }
slouken@797
   206
slouken@797
   207
    if (!music->play_count) {
slouken@797
   208
        /* All done */
slouken@797
   209
        *done = SDL_TRUE;
slouken@797
   210
        return 0;
slouken@797
   211
    }
slouken@797
   212
slouken@797
   213
    pos = SDL_RWtell(music->src);
slouken@797
   214
    stop = music->stop;
slouken@709
   215
    loop = NULL;
slouken@797
   216
    for (i = 0; i < music->numloops; ++i) {
slouken@797
   217
        loop = &music->loops[i];
slouken@709
   218
        if (loop->active) {
slouken@797
   219
            const int bytes_per_sample = (SDL_AUDIO_BITSIZE(music->spec.format) / 8) * music->spec.channels;
slouken@797
   220
            loop_start = music->start + loop->start * bytes_per_sample;
slouken@797
   221
            loop_stop = music->start + (loop->stop + 1) * bytes_per_sample;
slouken@709
   222
            if (pos >= loop_start && pos < loop_stop)
slouken@709
   223
            {
slouken@709
   224
                stop = loop_stop;
slouken@709
   225
                break;
slouken@709
   226
            }
slouken@709
   227
        }
slouken@709
   228
        loop = NULL;
slouken@709
   229
    }
slouken@709
   230
slouken@797
   231
    amount = music->spec.size;
slouken@797
   232
    if ((stop - pos) < amount) {
slouken@797
   233
        amount = (int)(stop - pos);
slouken@797
   234
    }
slouken@797
   235
    amount = (int)SDL_RWread(music->src, music->buffer, 1, amount);
slouken@797
   236
    if (amount > 0) {
slouken@797
   237
        result = SDL_AudioStreamPut(music->stream, music->buffer, amount);
slouken@797
   238
        if (result < 0) {
slouken@797
   239
            return -1;
slouken@729
   240
        }
slouken@709
   241
    } else {
slouken@797
   242
        /* We might be looping, continue */
slouken@709
   243
    }
slouken@709
   244
slouken@797
   245
    if (loop && SDL_RWtell(music->src) >= stop) {
slouken@709
   246
        if (loop->current_play_count == 1) {
slouken@709
   247
            loop->active = SDL_FALSE;
slouken@709
   248
        } else {
slouken@709
   249
            if (loop->current_play_count > 0) {
slouken@709
   250
                --loop->current_play_count;
slouken@709
   251
            }
slouken@797
   252
            SDL_RWseek(music->src, loop_start, RW_SEEK_SET);
slouken@797
   253
            looped = SDL_TRUE;
slouken@709
   254
        }
slouken@709
   255
    }
slouken@797
   256
slouken@797
   257
    if (!looped && SDL_RWtell(music->src) >= music->stop) {
slouken@797
   258
        if (music->play_count == 1) {
slouken@797
   259
            music->play_count = 0;
slouken@797
   260
            SDL_AudioStreamFlush(music->stream);
slouken@797
   261
        } else {
slouken@797
   262
            int play_count = -1;
slouken@797
   263
            if (music->play_count > 0) {
slouken@797
   264
                play_count = (music->play_count - 1);
slouken@797
   265
            }
slouken@797
   266
            if (WAV_Play(music, play_count) < 0) {
slouken@797
   267
                return -1;
slouken@797
   268
            }
slouken@797
   269
        }
slouken@797
   270
    }
slouken@797
   271
slouken@797
   272
    /* We'll get called again in the case where we looped or have more data */
slouken@797
   273
    return 0;
slouken@709
   274
}
slouken@709
   275
slouken@797
   276
static int WAV_GetAudio(void *context, void *data, int bytes)
slouken@0
   277
{
slouken@797
   278
    WAV_Music *music = (WAV_Music *)context;
slouken@797
   279
    return music_pcm_getaudio(context, data, bytes, music->volume, WAV_GetSome);
slouken@0
   280
}
slouken@0
   281
slouken@777
   282
/* Close the given WAV stream */
slouken@797
   283
static void WAV_Delete(void *context)
slouken@0
   284
{
slouken@797
   285
    WAV_Music *music = (WAV_Music *)context;
slouken@0
   286
slouken@777
   287
    /* Clean up associated data */
slouken@797
   288
    if (music->loops) {
slouken@797
   289
        SDL_free(music->loops);
slouken@617
   290
    }
slouken@797
   291
    if (music->stream) {
slouken@797
   292
        SDL_FreeAudioStream(music->stream);
slouken@617
   293
    }
slouken@797
   294
    if (music->buffer) {
slouken@797
   295
        SDL_free(music->buffer);
slouken@777
   296
    }
slouken@797
   297
    if (music->freesrc) {
slouken@797
   298
        SDL_RWclose(music->src);
slouken@797
   299
    }
slouken@797
   300
    SDL_free(music);
slouken@0
   301
}
slouken@0
   302
slouken@797
   303
static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length)
slouken@0
   304
{
slouken@709
   305
    SDL_AudioSpec *spec = &wave->spec;
slouken@709
   306
    WaveFMT *format;
slouken@709
   307
    Uint8 *data;
slouken@709
   308
    SDL_bool loaded = SDL_FALSE;
slouken@709
   309
slouken@709
   310
    if (chunk_length < sizeof(*format)) {
slouken@709
   311
        Mix_SetError("Wave format chunk too small");
slouken@709
   312
        return SDL_FALSE;
slouken@617
   313
    }
slouken@0
   314
slouken@709
   315
    data = (Uint8 *)SDL_malloc(chunk_length);
slouken@709
   316
    if (!data) {
slouken@709
   317
        Mix_SetError("Out of memory");
slouken@709
   318
        return SDL_FALSE;
slouken@709
   319
    }
slouken@709
   320
    if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
slouken@709
   321
        Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
slouken@709
   322
        return SDL_FALSE;
slouken@709
   323
    }
slouken@709
   324
    format = (WaveFMT *)data;
slouken@0
   325
slouken@617
   326
    /* Decode the audio data format */
slouken@617
   327
    switch (SDL_SwapLE16(format->encoding)) {
slouken@617
   328
        case PCM_CODE:
slouken@617
   329
            /* We can understand this */
slouken@617
   330
            break;
slouken@617
   331
        default:
slouken@617
   332
            Mix_SetError("Unknown WAVE data format");
slouken@617
   333
            goto done;
slouken@617
   334
    }
slouken@617
   335
    spec->freq = SDL_SwapLE32(format->frequency);
slouken@617
   336
    switch (SDL_SwapLE16(format->bitspersample)) {
slouken@617
   337
        case 8:
slouken@617
   338
            spec->format = AUDIO_U8;
slouken@617
   339
            break;
slouken@617
   340
        case 16:
slouken@617
   341
            spec->format = AUDIO_S16;
slouken@617
   342
            break;
slouken@617
   343
        default:
slouken@617
   344
            Mix_SetError("Unknown PCM data format");
slouken@617
   345
            goto done;
slouken@617
   346
    }
slouken@617
   347
    spec->channels = (Uint8) SDL_SwapLE16(format->channels);
slouken@617
   348
    spec->samples = 4096;       /* Good default buffer size */
slouken@797
   349
    /* SDL_CalculateAudioSpec */
slouken@797
   350
    spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
slouken@797
   351
    spec->size *= spec->channels;
slouken@797
   352
    spec->size *= spec->samples;
slouken@0
   353
slouken@709
   354
    loaded = SDL_TRUE;
slouken@0
   355
slouken@0
   356
done:
slouken@709
   357
    SDL_free(data);
slouken@709
   358
    return loaded;
slouken@709
   359
}
slouken@709
   360
slouken@797
   361
static SDL_bool ParseDATA(WAV_Music *wave, Uint32 chunk_length)
slouken@709
   362
{
slouken@709
   363
    wave->start = SDL_RWtell(wave->src);
slouken@709
   364
    wave->stop = wave->start + chunk_length;
slouken@709
   365
    SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR);
slouken@709
   366
    return SDL_TRUE;
slouken@709
   367
}
slouken@709
   368
slouken@797
   369
static SDL_bool AddLoopPoint(WAV_Music *wave, Uint32 play_count, Uint32 start, Uint32 stop)
slouken@709
   370
{
slouken@709
   371
    WAVLoopPoint *loop;
slouken@709
   372
    WAVLoopPoint *loops = SDL_realloc(wave->loops, (wave->numloops + 1)*sizeof(*wave->loops));
slouken@709
   373
    if (!loops) {
slouken@709
   374
        Mix_SetError("Out of memory");
slouken@709
   375
        return SDL_FALSE;
slouken@617
   376
    }
slouken@709
   377
slouken@709
   378
    loop = &loops[ wave->numloops ];
slouken@709
   379
    loop->start = start;
slouken@709
   380
    loop->stop = stop;
slouken@709
   381
    loop->initial_play_count = play_count;
slouken@709
   382
    loop->current_play_count = play_count;
slouken@709
   383
slouken@709
   384
    wave->loops = loops;
slouken@709
   385
    ++wave->numloops;
slouken@709
   386
    return SDL_TRUE;
slouken@709
   387
}
slouken@709
   388
slouken@797
   389
static SDL_bool ParseSMPL(WAV_Music *wave, Uint32 chunk_length)
slouken@709
   390
{
slouken@709
   391
    SamplerChunk *chunk;
slouken@709
   392
    Uint8 *data;
slouken@755
   393
    Uint32 i;
slouken@709
   394
    SDL_bool loaded = SDL_FALSE;
slouken@709
   395
slouken@709
   396
    data = (Uint8 *)SDL_malloc(chunk_length);
slouken@709
   397
    if (!data) {
slouken@709
   398
        Mix_SetError("Out of memory");
slouken@709
   399
        return SDL_FALSE;
slouken@617
   400
    }
slouken@709
   401
    if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
slouken@709
   402
        Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
slouken@709
   403
        return SDL_FALSE;
slouken@709
   404
    }
slouken@709
   405
    chunk = (SamplerChunk *)data;
slouken@709
   406
slouken@709
   407
    for (i = 0; i < SDL_SwapLE32(chunk->sample_loops); ++i) {
slouken@709
   408
        const Uint32 LOOP_TYPE_FORWARD = 0;
slouken@709
   409
        Uint32 loop_type = SDL_SwapLE32(chunk->loops[i].type);
slouken@709
   410
        if (loop_type == LOOP_TYPE_FORWARD) {
slouken@709
   411
            AddLoopPoint(wave, SDL_SwapLE32(chunk->loops[i].play_count), SDL_SwapLE32(chunk->loops[i].start), SDL_SwapLE32(chunk->loops[i].end));
slouken@709
   412
        }
slouken@709
   413
    }
slouken@709
   414
slouken@709
   415
    loaded = SDL_TRUE;
slouken@709
   416
    SDL_free(data);
slouken@709
   417
    return loaded;
slouken@709
   418
}
slouken@709
   419
slouken@797
   420
static SDL_bool LoadWAVMusic(WAV_Music *wave)
slouken@709
   421
{
slouken@709
   422
    SDL_RWops *src = wave->src;
slouken@709
   423
    Uint32 chunk_type;
slouken@709
   424
    Uint32 chunk_length;
slouken@709
   425
    SDL_bool found_FMT = SDL_FALSE;
slouken@709
   426
    SDL_bool found_DATA = SDL_FALSE;
slouken@709
   427
slouken@709
   428
    /* WAV magic header */
slouken@709
   429
    Uint32 wavelen;
slouken@709
   430
    Uint32 WAVEmagic;
slouken@709
   431
slouken@709
   432
    /* Check the magic header */
slouken@709
   433
    wavelen = SDL_ReadLE32(src);
slouken@709
   434
    WAVEmagic = SDL_ReadLE32(src);
slouken@709
   435
slouken@709
   436
    /* Read the chunks */
slouken@709
   437
    for (; ;) {
slouken@709
   438
        chunk_type = SDL_ReadLE32(src);
slouken@709
   439
        chunk_length = SDL_ReadLE32(src);
slouken@709
   440
slouken@709
   441
        if (chunk_length == 0)
slouken@709
   442
            break;
slouken@709
   443
slouken@709
   444
        switch (chunk_type)
slouken@709
   445
        {
slouken@709
   446
        case FMT:
slouken@709
   447
            found_FMT = SDL_TRUE;
slouken@709
   448
            if (!ParseFMT(wave, chunk_length))
slouken@709
   449
                return SDL_FALSE;
slouken@709
   450
            break;
slouken@709
   451
        case DATA:
slouken@709
   452
            found_DATA = SDL_TRUE;
slouken@709
   453
            if (!ParseDATA(wave, chunk_length))
slouken@709
   454
                return SDL_FALSE;
slouken@709
   455
            break;
slouken@709
   456
        case SMPL:
slouken@709
   457
            if (!ParseSMPL(wave, chunk_length))
slouken@709
   458
                return SDL_FALSE;
slouken@709
   459
            break;
slouken@709
   460
        default:
slouken@709
   461
            SDL_RWseek(src, chunk_length, RW_SEEK_CUR);
slouken@709
   462
            break;
slouken@709
   463
        }
slouken@709
   464
    }
slouken@709
   465
slouken@709
   466
    if (!found_FMT) {
slouken@709
   467
        Mix_SetError("Bad WAV file (no FMT chunk)");
slouken@709
   468
        return SDL_FALSE;
slouken@709
   469
    }
slouken@709
   470
slouken@709
   471
    if (!found_DATA) {
slouken@709
   472
        Mix_SetError("Bad WAV file (no DATA chunk)");
slouken@709
   473
        return SDL_FALSE;
slouken@709
   474
    }
slouken@709
   475
slouken@709
   476
    return SDL_TRUE;
slouken@0
   477
}
slouken@0
   478
slouken@95
   479
/* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
slouken@95
   480
 * I don't pretend to fully understand it.
slouken@95
   481
 */
slouken@95
   482
slouken@95
   483
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
slouken@0
   484
{
slouken@617
   485
    /* Negative number? */
slouken@617
   486
    if (sanebuf[0] & 0x80)
slouken@617
   487
        return 0;
slouken@0
   488
slouken@617
   489
    /* Less than 1? */
slouken@617
   490
    if (sanebuf[0] <= 0x3F)
slouken@617
   491
        return 1;
slouken@95
   492
slouken@617
   493
    /* Way too big? */
slouken@617
   494
    if (sanebuf[0] > 0x40)
slouken@617
   495
        return 0x4000000;
slouken@95
   496
slouken@617
   497
    /* Still too big? */
slouken@617
   498
    if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
slouken@617
   499
        return 800000000;
slouken@95
   500
slouken@709
   501
    return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) |
slouken@709
   502
            (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
slouken@0
   503
}
slouken@0
   504
slouken@797
   505
static SDL_bool LoadAIFFMusic(WAV_Music *wave)
slouken@0
   506
{
slouken@709
   507
    SDL_RWops *src = wave->src;
slouken@709
   508
    SDL_AudioSpec *spec = &wave->spec;
slouken@709
   509
    SDL_bool found_SSND = SDL_FALSE;
slouken@709
   510
    SDL_bool found_COMM = SDL_FALSE;
slouken@0
   511
slouken@617
   512
    Uint32 chunk_type;
slouken@617
   513
    Uint32 chunk_length;
slouken@621
   514
    Sint64 next_chunk;
slouken@95
   515
slouken@617
   516
    /* AIFF magic header */
slouken@617
   517
    Uint32 AIFFmagic;
slouken@617
   518
    /* SSND chunk        */
slouken@617
   519
    Uint32 offset;
slouken@617
   520
    Uint32 blocksize;
slouken@617
   521
    /* COMM format chunk */
slouken@617
   522
    Uint16 channels = 0;
slouken@617
   523
    Uint32 numsamples = 0;
slouken@617
   524
    Uint16 samplesize = 0;
slouken@617
   525
    Uint8 sane_freq[10];
slouken@617
   526
    Uint32 frequency = 0;
slouken@0
   527
slouken@617
   528
    /* Check the magic header */
slouken@709
   529
    chunk_length = SDL_ReadBE32(src);
slouken@709
   530
    AIFFmagic = SDL_ReadLE32(src);
slouken@709
   531
    if (AIFFmagic != AIFF) {
slouken@617
   532
        Mix_SetError("Unrecognized file type (not AIFF)");
slouken@709
   533
        return SDL_FALSE;
slouken@617
   534
    }
slouken@0
   535
slouken@617
   536
    /* From what I understand of the specification, chunks may appear in
slouken@709
   537
     * any order, and we should just ignore unknown ones.
slouken@617
   538
     *
slouken@617
   539
     * TODO: Better sanity-checking. E.g. what happens if the AIFF file
slouken@617
   540
     *       contains compressed sound data?
slouken@625
   541
     */
slouken@617
   542
    do {
slouken@617
   543
        chunk_type      = SDL_ReadLE32(src);
slouken@617
   544
        chunk_length    = SDL_ReadBE32(src);
slouken@617
   545
        next_chunk      = SDL_RWtell(src) + chunk_length;
slouken@95
   546
slouken@617
   547
        /* Paranoia to avoid infinite loops */
slouken@617
   548
        if (chunk_length == 0)
slouken@709
   549
            break;
slouken@95
   550
slouken@625
   551
        switch (chunk_type) {
slouken@617
   552
        case SSND:
slouken@709
   553
            found_SSND = SDL_TRUE;
slouken@709
   554
            offset = SDL_ReadBE32(src);
slouken@709
   555
            blocksize = SDL_ReadBE32(src);
slouken@709
   556
            wave->start = SDL_RWtell(src) + offset;
slouken@617
   557
            break;
slouken@95
   558
slouken@617
   559
        case COMM:
slouken@709
   560
            found_COMM = SDL_TRUE;
slouken@95
   561
slouken@617
   562
            /* Read the audio data format chunk */
slouken@709
   563
            channels = SDL_ReadBE16(src);
slouken@709
   564
            numsamples = SDL_ReadBE32(src);
slouken@709
   565
            samplesize = SDL_ReadBE16(src);
slouken@617
   566
            SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
slouken@709
   567
            frequency = SANE_to_Uint32(sane_freq);
slouken@617
   568
            break;
slouken@95
   569
slouken@617
   570
        default:
slouken@617
   571
            break;
slouken@617
   572
        }
slouken@617
   573
    } while ((!found_SSND || !found_COMM)
slouken@617
   574
         && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
slouken@95
   575
slouken@617
   576
    if (!found_SSND) {
slouken@617
   577
        Mix_SetError("Bad AIFF file (no SSND chunk)");
slouken@709
   578
        return SDL_FALSE;
slouken@617
   579
    }
slouken@0
   580
slouken@617
   581
    if (!found_COMM) {
slouken@617
   582
        Mix_SetError("Bad AIFF file (no COMM chunk)");
slouken@709
   583
        return SDL_FALSE;
slouken@617
   584
    }
slouken@0
   585
slouken@709
   586
    wave->stop = wave->start + channels * numsamples * (samplesize / 8);
slouken@617
   587
slouken@617
   588
    /* Decode the audio data format */
slouken@621
   589
    SDL_memset(spec, 0, (sizeof *spec));
slouken@617
   590
    spec->freq = frequency;
slouken@617
   591
    switch (samplesize) {
slouken@617
   592
        case 8:
slouken@617
   593
            spec->format = AUDIO_S8;
slouken@617
   594
            break;
slouken@617
   595
        case 16:
slouken@617
   596
            spec->format = AUDIO_S16MSB;
slouken@617
   597
            break;
slouken@617
   598
        default:
slouken@617
   599
            Mix_SetError("Unknown samplesize in data format");
slouken@709
   600
            return SDL_FALSE;
slouken@617
   601
    }
slouken@617
   602
    spec->channels = (Uint8) channels;
slouken@617
   603
    spec->samples = 4096;       /* Good default buffer size */
slouken@0
   604
slouken@709
   605
    return SDL_TRUE;
slouken@0
   606
}
slouken@308
   607
slouken@777
   608
Mix_MusicInterface Mix_MusicInterface_WAV =
slouken@777
   609
{
slouken@777
   610
    "WAVE",
slouken@777
   611
    MIX_MUSIC_WAVE,
slouken@777
   612
    MUS_WAV,
slouken@777
   613
    SDL_FALSE,
slouken@777
   614
    SDL_FALSE,
slouken@777
   615
slouken@777
   616
    NULL,   /* Load */
slouken@777
   617
    NULL,   /* Open */
slouken@797
   618
    WAV_CreateFromRW,
slouken@777
   619
    NULL,   /* CreateFromFile */
slouken@797
   620
    WAV_SetVolume,
slouken@797
   621
    WAV_Play,
slouken@777
   622
    NULL,   /* IsPlaying */
slouken@797
   623
    WAV_GetAudio,
slouken@777
   624
    NULL,   /* Seek */
slouken@777
   625
    NULL,   /* Pause */
slouken@777
   626
    NULL,   /* Resume */
slouken@777
   627
    NULL,   /* Stop */
slouken@797
   628
    WAV_Delete,
slouken@777
   629
    NULL,   /* Close */
slouken@777
   630
    NULL,   /* Unload */
slouken@777
   631
};
slouken@777
   632
slouken@777
   633
#endif /* MUSIC_WAV */
slouken@777
   634
slouken@777
   635
/* vi: set ts=4 sw=4 expandtab: */