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