music_wav.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 01 Mar 2018 09:34:14 -0800
changeset 848 3907db698eb5
parent 826 2b5e37881cba
child 926 d6c9518fb5ee
permissions -rw-r--r--
Updated copyright for 2018
slouken@0
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@848
     3
  Copyright (C) 1997-2018 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@826
   108
    SampleLoop loops[1];
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@814
   138
    music->src = src;
slouken@797
   139
    music->volume = MIX_MAX_VOLUME;
slouken@625
   140
slouken@797
   141
    magic = SDL_ReadLE32(src);
slouken@797
   142
    if (magic == RIFF || magic == WAVE) {
slouken@797
   143
        loaded = LoadWAVMusic(music);
slouken@797
   144
    } else if (magic == FORM) {
slouken@797
   145
        loaded = LoadAIFFMusic(music);
slouken@797
   146
    } else {
slouken@797
   147
        Mix_SetError("Unknown WAVE format");
slouken@797
   148
    }
slouken@797
   149
    if (!loaded) {
slouken@797
   150
        SDL_free(music);
slouken@797
   151
        return NULL;
slouken@797
   152
    }
slouken@797
   153
    music->buffer = (Uint8*)SDL_malloc(music->spec.size);
slouken@797
   154
    if (!music->buffer) {
slouken@797
   155
        WAV_Delete(music);
slouken@797
   156
        return NULL;
slouken@797
   157
    }
slouken@797
   158
    music->stream = SDL_NewAudioStream(
slouken@797
   159
        music->spec.format, music->spec.channels, music->spec.freq,
slouken@797
   160
        music_spec.format, music_spec.channels, music_spec.freq);
slouken@797
   161
    if (!music->stream) {
slouken@797
   162
        WAV_Delete(music);
slouken@797
   163
        return NULL;
slouken@797
   164
    }
slouken@625
   165
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@800
   187
    if (SDL_RWseek(music->src, music->start, RW_SEEK_SET) < 0) {
slouken@800
   188
        return -1;
slouken@800
   189
    }
slouken@800
   190
    return 0;
slouken@0
   191
}
slouken@0
   192
slouken@797
   193
/* Play some of a stream previously started with WAV_Play() */
slouken@797
   194
static int WAV_GetSome(void *context, void *data, int bytes, SDL_bool *done)
slouken@709
   195
{
slouken@797
   196
    WAV_Music *music = (WAV_Music *)context;
slouken@709
   197
    Sint64 pos, stop;
slouken@709
   198
    WAVLoopPoint *loop;
slouken@709
   199
    Sint64 loop_start;
slouken@709
   200
    Sint64 loop_stop;
slouken@797
   201
    SDL_bool looped = SDL_FALSE;
slouken@709
   202
    int i;
slouken@797
   203
    int filled, amount, result;
slouken@709
   204
slouken@797
   205
    filled = SDL_AudioStreamGet(music->stream, data, bytes);
slouken@797
   206
    if (filled != 0) {
slouken@797
   207
        return filled;
slouken@797
   208
    }
slouken@797
   209
slouken@797
   210
    if (!music->play_count) {
slouken@797
   211
        /* All done */
slouken@797
   212
        *done = SDL_TRUE;
slouken@797
   213
        return 0;
slouken@797
   214
    }
slouken@797
   215
slouken@797
   216
    pos = SDL_RWtell(music->src);
slouken@797
   217
    stop = music->stop;
slouken@709
   218
    loop = NULL;
slouken@797
   219
    for (i = 0; i < music->numloops; ++i) {
slouken@797
   220
        loop = &music->loops[i];
slouken@709
   221
        if (loop->active) {
slouken@797
   222
            const int bytes_per_sample = (SDL_AUDIO_BITSIZE(music->spec.format) / 8) * music->spec.channels;
slouken@797
   223
            loop_start = music->start + loop->start * bytes_per_sample;
slouken@797
   224
            loop_stop = music->start + (loop->stop + 1) * bytes_per_sample;
slouken@709
   225
            if (pos >= loop_start && pos < loop_stop)
slouken@709
   226
            {
slouken@709
   227
                stop = loop_stop;
slouken@709
   228
                break;
slouken@709
   229
            }
slouken@709
   230
        }
slouken@709
   231
        loop = NULL;
slouken@709
   232
    }
slouken@709
   233
slouken@797
   234
    amount = music->spec.size;
slouken@797
   235
    if ((stop - pos) < amount) {
slouken@797
   236
        amount = (int)(stop - pos);
slouken@797
   237
    }
slouken@797
   238
    amount = (int)SDL_RWread(music->src, music->buffer, 1, amount);
slouken@797
   239
    if (amount > 0) {
slouken@797
   240
        result = SDL_AudioStreamPut(music->stream, music->buffer, amount);
slouken@797
   241
        if (result < 0) {
slouken@797
   242
            return -1;
slouken@729
   243
        }
slouken@709
   244
    } else {
slouken@797
   245
        /* We might be looping, continue */
slouken@709
   246
    }
slouken@709
   247
slouken@797
   248
    if (loop && SDL_RWtell(music->src) >= stop) {
slouken@709
   249
        if (loop->current_play_count == 1) {
slouken@709
   250
            loop->active = SDL_FALSE;
slouken@709
   251
        } else {
slouken@709
   252
            if (loop->current_play_count > 0) {
slouken@709
   253
                --loop->current_play_count;
slouken@709
   254
            }
slouken@797
   255
            SDL_RWseek(music->src, loop_start, RW_SEEK_SET);
slouken@797
   256
            looped = SDL_TRUE;
slouken@709
   257
        }
slouken@709
   258
    }
slouken@797
   259
slouken@797
   260
    if (!looped && SDL_RWtell(music->src) >= music->stop) {
slouken@797
   261
        if (music->play_count == 1) {
slouken@797
   262
            music->play_count = 0;
slouken@797
   263
            SDL_AudioStreamFlush(music->stream);
slouken@797
   264
        } else {
slouken@797
   265
            int play_count = -1;
slouken@797
   266
            if (music->play_count > 0) {
slouken@797
   267
                play_count = (music->play_count - 1);
slouken@797
   268
            }
slouken@797
   269
            if (WAV_Play(music, play_count) < 0) {
slouken@797
   270
                return -1;
slouken@797
   271
            }
slouken@797
   272
        }
slouken@797
   273
    }
slouken@797
   274
slouken@797
   275
    /* We'll get called again in the case where we looped or have more data */
slouken@797
   276
    return 0;
slouken@709
   277
}
slouken@709
   278
slouken@797
   279
static int WAV_GetAudio(void *context, void *data, int bytes)
slouken@0
   280
{
slouken@797
   281
    WAV_Music *music = (WAV_Music *)context;
slouken@797
   282
    return music_pcm_getaudio(context, data, bytes, music->volume, WAV_GetSome);
slouken@0
   283
}
slouken@0
   284
slouken@777
   285
/* Close the given WAV stream */
slouken@797
   286
static void WAV_Delete(void *context)
slouken@0
   287
{
slouken@797
   288
    WAV_Music *music = (WAV_Music *)context;
slouken@0
   289
slouken@777
   290
    /* Clean up associated data */
slouken@797
   291
    if (music->loops) {
slouken@797
   292
        SDL_free(music->loops);
slouken@617
   293
    }
slouken@797
   294
    if (music->stream) {
slouken@797
   295
        SDL_FreeAudioStream(music->stream);
slouken@617
   296
    }
slouken@797
   297
    if (music->buffer) {
slouken@797
   298
        SDL_free(music->buffer);
slouken@777
   299
    }
slouken@797
   300
    if (music->freesrc) {
slouken@797
   301
        SDL_RWclose(music->src);
slouken@797
   302
    }
slouken@797
   303
    SDL_free(music);
slouken@0
   304
}
slouken@0
   305
slouken@797
   306
static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length)
slouken@0
   307
{
slouken@709
   308
    SDL_AudioSpec *spec = &wave->spec;
slouken@709
   309
    WaveFMT *format;
slouken@709
   310
    Uint8 *data;
slouken@709
   311
    SDL_bool loaded = SDL_FALSE;
slouken@709
   312
slouken@709
   313
    if (chunk_length < sizeof(*format)) {
slouken@709
   314
        Mix_SetError("Wave format chunk too small");
slouken@709
   315
        return SDL_FALSE;
slouken@617
   316
    }
slouken@0
   317
slouken@709
   318
    data = (Uint8 *)SDL_malloc(chunk_length);
slouken@709
   319
    if (!data) {
slouken@709
   320
        Mix_SetError("Out of memory");
slouken@709
   321
        return SDL_FALSE;
slouken@709
   322
    }
slouken@709
   323
    if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
slouken@709
   324
        Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
slouken@709
   325
        return SDL_FALSE;
slouken@709
   326
    }
slouken@709
   327
    format = (WaveFMT *)data;
slouken@0
   328
slouken@617
   329
    /* Decode the audio data format */
slouken@617
   330
    switch (SDL_SwapLE16(format->encoding)) {
slouken@617
   331
        case PCM_CODE:
slouken@617
   332
            /* We can understand this */
slouken@617
   333
            break;
slouken@617
   334
        default:
slouken@617
   335
            Mix_SetError("Unknown WAVE data format");
slouken@617
   336
            goto done;
slouken@617
   337
    }
slouken@617
   338
    spec->freq = SDL_SwapLE32(format->frequency);
slouken@617
   339
    switch (SDL_SwapLE16(format->bitspersample)) {
slouken@617
   340
        case 8:
slouken@617
   341
            spec->format = AUDIO_U8;
slouken@617
   342
            break;
slouken@617
   343
        case 16:
slouken@617
   344
            spec->format = AUDIO_S16;
slouken@617
   345
            break;
slouken@617
   346
        default:
slouken@617
   347
            Mix_SetError("Unknown PCM data format");
slouken@617
   348
            goto done;
slouken@617
   349
    }
slouken@617
   350
    spec->channels = (Uint8) SDL_SwapLE16(format->channels);
slouken@617
   351
    spec->samples = 4096;       /* Good default buffer size */
slouken@797
   352
    /* SDL_CalculateAudioSpec */
slouken@797
   353
    spec->size = SDL_AUDIO_BITSIZE(spec->format) / 8;
slouken@797
   354
    spec->size *= spec->channels;
slouken@797
   355
    spec->size *= spec->samples;
slouken@0
   356
slouken@709
   357
    loaded = SDL_TRUE;
slouken@0
   358
slouken@0
   359
done:
slouken@709
   360
    SDL_free(data);
slouken@709
   361
    return loaded;
slouken@709
   362
}
slouken@709
   363
slouken@797
   364
static SDL_bool ParseDATA(WAV_Music *wave, Uint32 chunk_length)
slouken@709
   365
{
slouken@709
   366
    wave->start = SDL_RWtell(wave->src);
slouken@709
   367
    wave->stop = wave->start + chunk_length;
slouken@709
   368
    SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR);
slouken@709
   369
    return SDL_TRUE;
slouken@709
   370
}
slouken@709
   371
slouken@797
   372
static SDL_bool AddLoopPoint(WAV_Music *wave, Uint32 play_count, Uint32 start, Uint32 stop)
slouken@709
   373
{
slouken@709
   374
    WAVLoopPoint *loop;
slouken@709
   375
    WAVLoopPoint *loops = SDL_realloc(wave->loops, (wave->numloops + 1)*sizeof(*wave->loops));
slouken@709
   376
    if (!loops) {
slouken@709
   377
        Mix_SetError("Out of memory");
slouken@709
   378
        return SDL_FALSE;
slouken@617
   379
    }
slouken@709
   380
slouken@709
   381
    loop = &loops[ wave->numloops ];
slouken@709
   382
    loop->start = start;
slouken@709
   383
    loop->stop = stop;
slouken@709
   384
    loop->initial_play_count = play_count;
slouken@709
   385
    loop->current_play_count = play_count;
slouken@709
   386
slouken@709
   387
    wave->loops = loops;
slouken@709
   388
    ++wave->numloops;
slouken@709
   389
    return SDL_TRUE;
slouken@709
   390
}
slouken@709
   391
slouken@797
   392
static SDL_bool ParseSMPL(WAV_Music *wave, Uint32 chunk_length)
slouken@709
   393
{
slouken@709
   394
    SamplerChunk *chunk;
slouken@709
   395
    Uint8 *data;
slouken@755
   396
    Uint32 i;
slouken@709
   397
    SDL_bool loaded = SDL_FALSE;
slouken@709
   398
slouken@709
   399
    data = (Uint8 *)SDL_malloc(chunk_length);
slouken@709
   400
    if (!data) {
slouken@709
   401
        Mix_SetError("Out of memory");
slouken@709
   402
        return SDL_FALSE;
slouken@617
   403
    }
slouken@709
   404
    if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
slouken@709
   405
        Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
slouken@709
   406
        return SDL_FALSE;
slouken@709
   407
    }
slouken@709
   408
    chunk = (SamplerChunk *)data;
slouken@709
   409
slouken@709
   410
    for (i = 0; i < SDL_SwapLE32(chunk->sample_loops); ++i) {
slouken@709
   411
        const Uint32 LOOP_TYPE_FORWARD = 0;
slouken@709
   412
        Uint32 loop_type = SDL_SwapLE32(chunk->loops[i].type);
slouken@709
   413
        if (loop_type == LOOP_TYPE_FORWARD) {
slouken@709
   414
            AddLoopPoint(wave, SDL_SwapLE32(chunk->loops[i].play_count), SDL_SwapLE32(chunk->loops[i].start), SDL_SwapLE32(chunk->loops[i].end));
slouken@709
   415
        }
slouken@709
   416
    }
slouken@709
   417
slouken@709
   418
    loaded = SDL_TRUE;
slouken@709
   419
    SDL_free(data);
slouken@709
   420
    return loaded;
slouken@709
   421
}
slouken@709
   422
slouken@797
   423
static SDL_bool LoadWAVMusic(WAV_Music *wave)
slouken@709
   424
{
slouken@709
   425
    SDL_RWops *src = wave->src;
slouken@709
   426
    Uint32 chunk_type;
slouken@709
   427
    Uint32 chunk_length;
slouken@709
   428
    SDL_bool found_FMT = SDL_FALSE;
slouken@709
   429
    SDL_bool found_DATA = SDL_FALSE;
slouken@709
   430
slouken@709
   431
    /* WAV magic header */
slouken@709
   432
    Uint32 wavelen;
slouken@709
   433
    Uint32 WAVEmagic;
slouken@709
   434
slouken@709
   435
    /* Check the magic header */
slouken@709
   436
    wavelen = SDL_ReadLE32(src);
slouken@709
   437
    WAVEmagic = SDL_ReadLE32(src);
slouken@709
   438
slouken@709
   439
    /* Read the chunks */
slouken@709
   440
    for (; ;) {
slouken@709
   441
        chunk_type = SDL_ReadLE32(src);
slouken@709
   442
        chunk_length = SDL_ReadLE32(src);
slouken@709
   443
slouken@709
   444
        if (chunk_length == 0)
slouken@709
   445
            break;
slouken@709
   446
slouken@709
   447
        switch (chunk_type)
slouken@709
   448
        {
slouken@709
   449
        case FMT:
slouken@709
   450
            found_FMT = SDL_TRUE;
slouken@709
   451
            if (!ParseFMT(wave, chunk_length))
slouken@709
   452
                return SDL_FALSE;
slouken@709
   453
            break;
slouken@709
   454
        case DATA:
slouken@709
   455
            found_DATA = SDL_TRUE;
slouken@709
   456
            if (!ParseDATA(wave, chunk_length))
slouken@709
   457
                return SDL_FALSE;
slouken@709
   458
            break;
slouken@709
   459
        case SMPL:
slouken@709
   460
            if (!ParseSMPL(wave, chunk_length))
slouken@709
   461
                return SDL_FALSE;
slouken@709
   462
            break;
slouken@709
   463
        default:
slouken@709
   464
            SDL_RWseek(src, chunk_length, RW_SEEK_CUR);
slouken@709
   465
            break;
slouken@709
   466
        }
slouken@709
   467
    }
slouken@709
   468
slouken@709
   469
    if (!found_FMT) {
slouken@709
   470
        Mix_SetError("Bad WAV file (no FMT chunk)");
slouken@709
   471
        return SDL_FALSE;
slouken@709
   472
    }
slouken@709
   473
slouken@709
   474
    if (!found_DATA) {
slouken@709
   475
        Mix_SetError("Bad WAV file (no DATA chunk)");
slouken@709
   476
        return SDL_FALSE;
slouken@709
   477
    }
slouken@709
   478
slouken@709
   479
    return SDL_TRUE;
slouken@0
   480
}
slouken@0
   481
slouken@95
   482
/* I couldn't get SANE_to_double() to work, so I stole this from libsndfile.
slouken@95
   483
 * I don't pretend to fully understand it.
slouken@95
   484
 */
slouken@95
   485
slouken@95
   486
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
slouken@0
   487
{
slouken@617
   488
    /* Negative number? */
slouken@617
   489
    if (sanebuf[0] & 0x80)
slouken@617
   490
        return 0;
slouken@0
   491
slouken@617
   492
    /* Less than 1? */
slouken@617
   493
    if (sanebuf[0] <= 0x3F)
slouken@617
   494
        return 1;
slouken@95
   495
slouken@617
   496
    /* Way too big? */
slouken@617
   497
    if (sanebuf[0] > 0x40)
slouken@617
   498
        return 0x4000000;
slouken@95
   499
slouken@617
   500
    /* Still too big? */
slouken@617
   501
    if (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C)
slouken@617
   502
        return 800000000;
slouken@95
   503
slouken@709
   504
    return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) |
slouken@709
   505
            (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
slouken@0
   506
}
slouken@0
   507
slouken@797
   508
static SDL_bool LoadAIFFMusic(WAV_Music *wave)
slouken@0
   509
{
slouken@709
   510
    SDL_RWops *src = wave->src;
slouken@709
   511
    SDL_AudioSpec *spec = &wave->spec;
slouken@709
   512
    SDL_bool found_SSND = SDL_FALSE;
slouken@709
   513
    SDL_bool found_COMM = SDL_FALSE;
slouken@0
   514
slouken@617
   515
    Uint32 chunk_type;
slouken@617
   516
    Uint32 chunk_length;
slouken@621
   517
    Sint64 next_chunk;
slouken@95
   518
slouken@617
   519
    /* AIFF magic header */
slouken@617
   520
    Uint32 AIFFmagic;
slouken@617
   521
    /* SSND chunk        */
slouken@617
   522
    Uint32 offset;
slouken@617
   523
    Uint32 blocksize;
slouken@617
   524
    /* COMM format chunk */
slouken@617
   525
    Uint16 channels = 0;
slouken@617
   526
    Uint32 numsamples = 0;
slouken@617
   527
    Uint16 samplesize = 0;
slouken@617
   528
    Uint8 sane_freq[10];
slouken@617
   529
    Uint32 frequency = 0;
slouken@0
   530
slouken@617
   531
    /* Check the magic header */
slouken@709
   532
    chunk_length = SDL_ReadBE32(src);
slouken@709
   533
    AIFFmagic = SDL_ReadLE32(src);
slouken@709
   534
    if (AIFFmagic != AIFF) {
slouken@617
   535
        Mix_SetError("Unrecognized file type (not AIFF)");
slouken@709
   536
        return SDL_FALSE;
slouken@617
   537
    }
slouken@0
   538
slouken@617
   539
    /* From what I understand of the specification, chunks may appear in
slouken@709
   540
     * any order, and we should just ignore unknown ones.
slouken@617
   541
     *
slouken@617
   542
     * TODO: Better sanity-checking. E.g. what happens if the AIFF file
slouken@617
   543
     *       contains compressed sound data?
slouken@625
   544
     */
slouken@617
   545
    do {
slouken@617
   546
        chunk_type      = SDL_ReadLE32(src);
slouken@617
   547
        chunk_length    = SDL_ReadBE32(src);
slouken@617
   548
        next_chunk      = SDL_RWtell(src) + chunk_length;
slouken@95
   549
slouken@617
   550
        /* Paranoia to avoid infinite loops */
slouken@617
   551
        if (chunk_length == 0)
slouken@709
   552
            break;
slouken@95
   553
slouken@625
   554
        switch (chunk_type) {
slouken@617
   555
        case SSND:
slouken@709
   556
            found_SSND = SDL_TRUE;
slouken@709
   557
            offset = SDL_ReadBE32(src);
slouken@709
   558
            blocksize = SDL_ReadBE32(src);
slouken@709
   559
            wave->start = SDL_RWtell(src) + offset;
slouken@617
   560
            break;
slouken@95
   561
slouken@617
   562
        case COMM:
slouken@709
   563
            found_COMM = SDL_TRUE;
slouken@95
   564
slouken@617
   565
            /* Read the audio data format chunk */
slouken@709
   566
            channels = SDL_ReadBE16(src);
slouken@709
   567
            numsamples = SDL_ReadBE32(src);
slouken@709
   568
            samplesize = SDL_ReadBE16(src);
slouken@617
   569
            SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
slouken@709
   570
            frequency = SANE_to_Uint32(sane_freq);
slouken@617
   571
            break;
slouken@95
   572
slouken@617
   573
        default:
slouken@617
   574
            break;
slouken@617
   575
        }
slouken@617
   576
    } while ((!found_SSND || !found_COMM)
slouken@617
   577
         && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != -1);
slouken@95
   578
slouken@617
   579
    if (!found_SSND) {
slouken@617
   580
        Mix_SetError("Bad AIFF file (no SSND chunk)");
slouken@709
   581
        return SDL_FALSE;
slouken@617
   582
    }
slouken@0
   583
slouken@617
   584
    if (!found_COMM) {
slouken@617
   585
        Mix_SetError("Bad AIFF file (no COMM chunk)");
slouken@709
   586
        return SDL_FALSE;
slouken@617
   587
    }
slouken@0
   588
slouken@709
   589
    wave->stop = wave->start + channels * numsamples * (samplesize / 8);
slouken@617
   590
slouken@617
   591
    /* Decode the audio data format */
slouken@621
   592
    SDL_memset(spec, 0, (sizeof *spec));
slouken@617
   593
    spec->freq = frequency;
slouken@617
   594
    switch (samplesize) {
slouken@617
   595
        case 8:
slouken@617
   596
            spec->format = AUDIO_S8;
slouken@617
   597
            break;
slouken@617
   598
        case 16:
slouken@617
   599
            spec->format = AUDIO_S16MSB;
slouken@617
   600
            break;
slouken@617
   601
        default:
slouken@617
   602
            Mix_SetError("Unknown samplesize in data format");
slouken@709
   603
            return SDL_FALSE;
slouken@617
   604
    }
slouken@617
   605
    spec->channels = (Uint8) channels;
slouken@617
   606
    spec->samples = 4096;       /* Good default buffer size */
slouken@0
   607
slouken@709
   608
    return SDL_TRUE;
slouken@0
   609
}
slouken@308
   610
slouken@777
   611
Mix_MusicInterface Mix_MusicInterface_WAV =
slouken@777
   612
{
slouken@777
   613
    "WAVE",
slouken@777
   614
    MIX_MUSIC_WAVE,
slouken@777
   615
    MUS_WAV,
slouken@777
   616
    SDL_FALSE,
slouken@777
   617
    SDL_FALSE,
slouken@777
   618
slouken@777
   619
    NULL,   /* Load */
slouken@777
   620
    NULL,   /* Open */
slouken@797
   621
    WAV_CreateFromRW,
slouken@777
   622
    NULL,   /* CreateFromFile */
slouken@797
   623
    WAV_SetVolume,
slouken@797
   624
    WAV_Play,
slouken@777
   625
    NULL,   /* IsPlaying */
slouken@797
   626
    WAV_GetAudio,
slouken@777
   627
    NULL,   /* Seek */
slouken@777
   628
    NULL,   /* Pause */
slouken@777
   629
    NULL,   /* Resume */
slouken@777
   630
    NULL,   /* Stop */
slouken@797
   631
    WAV_Delete,
slouken@777
   632
    NULL,   /* Close */
slouken@777
   633
    NULL,   /* Unload */
slouken@777
   634
};
slouken@777
   635
slouken@777
   636
#endif /* MUSIC_WAV */
slouken@777
   637
slouken@777
   638
/* vi: set ts=4 sw=4 expandtab: */