load_aiff.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 17 Oct 2017 02:33:47 -0700
changeset 777 92882ef2ab81
parent 725 bdf7b8d20566
child 848 3907db698eb5
permissions -rw-r--r--
Rewrote music.c to support any number of decode libraries using a compiled-in plugin interface
Mix_LoadWAV_RW() can now load sound formats that were previously available only as music.

This is still work in progress. Testing and project updates need to happen on other platforms.
slouken@111
     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@111
     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@111
     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@111
    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@111
    20
slouken@518
    21
  This is the source needed to decode an AIFF file into a waveform.
slouken@518
    22
  It's pretty straightforward once you get going. The only
slouken@518
    23
  externally-callable function is Mix_LoadAIFF_RW(), which is meant to
slouken@518
    24
  act as identically to SDL_LoadWAV_RW() as possible.
slouken@111
    25
slouken@518
    26
  This file by Torbjrn Andersson (torbjorn.andersson@eurotime.se)
slouken@518
    27
  8SVX file support added by Marc Le Douarain (mavati@club-internet.fr)
slouken@518
    28
  in december 2002.
slouken@111
    29
*/
slouken@111
    30
slouken@142
    31
#include "SDL_endian.h"
slouken@111
    32
#include "SDL_mixer.h"
slouken@111
    33
#include "load_aiff.h"
slouken@111
    34
slouken@111
    35
/*********************************************/
slouken@111
    36
/* Define values for AIFF (IFF audio) format */
slouken@111
    37
/*********************************************/
slouken@617
    38
#define FORM        0x4d524f46      /* "FORM" */
slouken@216
    39
slouken@617
    40
#define AIFF        0x46464941      /* "AIFF" */
slouken@617
    41
#define SSND        0x444e5353      /* "SSND" */
slouken@617
    42
#define COMM        0x4d4d4f43      /* "COMM" */
slouken@111
    43
slouken@617
    44
#define _8SVX       0x58565338      /* "8SVX" */
slouken@617
    45
#define VHDR        0x52444856      /* "VHDR" */
slouken@617
    46
#define BODY        0x59444F42      /* "BODY" */
slouken@216
    47
slouken@111
    48
/* This function was taken from libsndfile. I don't pretend to fully
slouken@111
    49
 * understand it.
slouken@111
    50
 */
slouken@111
    51
slouken@111
    52
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
slouken@111
    53
{
slouken@617
    54
    /* Is the frequency outside of what we can represent with Uint32? */
slouken@777
    55
    if ((sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40)
slouken@777
    56
        || (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C))
slouken@617
    57
        return 0;
slouken@111
    58
slouken@617
    59
    return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
slouken@617
    60
        | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
slouken@111
    61
}
slouken@111
    62
slouken@111
    63
/* This function is based on SDL_LoadWAV_RW(). */
slouken@111
    64
slouken@111
    65
SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc,
slouken@617
    66
    SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
slouken@111
    67
{
slouken@617
    68
    int was_error;
slouken@617
    69
    int found_SSND;
slouken@617
    70
    int found_COMM;
slouken@617
    71
    int found_VHDR;
slouken@617
    72
    int found_BODY;
slouken@621
    73
    Sint64 start = 0;
slouken@111
    74
slouken@617
    75
    Uint32 chunk_type;
slouken@617
    76
    Uint32 chunk_length;
slouken@621
    77
    Sint64 next_chunk;
slouken@111
    78
slouken@617
    79
    /* AIFF magic header */
slouken@617
    80
    Uint32 FORMchunk;
slouken@617
    81
    Uint32 AIFFmagic;
slouken@111
    82
slouken@617
    83
    /* SSND chunk */
slouken@617
    84
    Uint32 offset;
slouken@617
    85
    Uint32 blocksize;
slouken@111
    86
slouken@617
    87
    /* COMM format chunk */
slouken@617
    88
    Uint16 channels = 0;
slouken@617
    89
    Uint32 numsamples = 0;
slouken@617
    90
    Uint16 samplesize = 0;
slouken@617
    91
    Uint8 sane_freq[10];
slouken@617
    92
    Uint32 frequency = 0;
slouken@111
    93
slouken@617
    94
    /* Make sure we are passed a valid data source */
slouken@617
    95
    was_error = 0;
slouken@777
    96
    if (src == NULL) {
slouken@617
    97
        was_error = 1;
slouken@617
    98
        goto done;
slouken@617
    99
    }
slouken@111
   100
slouken@617
   101
    FORMchunk   = SDL_ReadLE32(src);
slouken@617
   102
    chunk_length    = SDL_ReadBE32(src);
slouken@777
   103
    if (chunk_length == AIFF) { /* The FORMchunk has already been read */
slouken@617
   104
        AIFFmagic    = chunk_length;
slouken@617
   105
        chunk_length = FORMchunk;
slouken@617
   106
        FORMchunk    = FORM;
slouken@617
   107
    } else {
slouken@617
   108
        AIFFmagic    = SDL_ReadLE32(src);
slouken@617
   109
    }
slouken@777
   110
    if ((FORMchunk != FORM) || ((AIFFmagic != AIFF) && (AIFFmagic != _8SVX))) {
slouken@617
   111
        SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)");
slouken@617
   112
        was_error = 1;
slouken@617
   113
        goto done;
slouken@617
   114
    }
slouken@111
   115
slouken@617
   116
    /* TODO: Better santity-checking. */
slouken@111
   117
slouken@617
   118
    found_SSND = 0;
slouken@617
   119
    found_COMM = 0;
slouken@617
   120
    found_VHDR = 0;
slouken@617
   121
    found_BODY = 0;
slouken@111
   122
slouken@617
   123
    do {
slouken@617
   124
        chunk_type  = SDL_ReadLE32(src);
slouken@621
   125
        chunk_length = SDL_ReadBE32(src);
slouken@617
   126
        next_chunk  = SDL_RWtell(src) + chunk_length;
slouken@617
   127
        /* Paranoia to avoid infinite loops */
slouken@617
   128
        if (chunk_length == 0)
slouken@617
   129
            break;
slouken@111
   130
slouken@617
   131
        switch (chunk_type) {
slouken@617
   132
            case SSND:
slouken@617
   133
                found_SSND  = 1;
slouken@617
   134
                offset      = SDL_ReadBE32(src);
slouken@617
   135
                blocksize   = SDL_ReadBE32(src);
slouken@617
   136
                start       = SDL_RWtell(src) + offset;
slouken@617
   137
                break;
slouken@111
   138
slouken@617
   139
            case COMM:
slouken@617
   140
                found_COMM  = 1;
slouken@617
   141
                channels    = SDL_ReadBE16(src);
slouken@617
   142
                numsamples  = SDL_ReadBE32(src);
slouken@617
   143
                samplesize  = SDL_ReadBE16(src);
slouken@617
   144
                SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
slouken@617
   145
                frequency   = SANE_to_Uint32(sane_freq);
slouken@617
   146
                if (frequency == 0) {
slouken@617
   147
                    SDL_SetError("Bad AIFF sample frequency");
slouken@617
   148
                    was_error = 1;
slouken@617
   149
                    goto done;
slouken@617
   150
                }
slouken@617
   151
                break;
slouken@111
   152
slouken@617
   153
            case VHDR:
slouken@617
   154
                found_VHDR  = 1;
slouken@617
   155
                SDL_ReadBE32(src);
slouken@617
   156
                SDL_ReadBE32(src);
slouken@617
   157
                SDL_ReadBE32(src);
slouken@617
   158
                frequency = SDL_ReadBE16(src);
slouken@617
   159
                channels = 1;
slouken@617
   160
                samplesize = 8;
slouken@617
   161
                break;
slouken@216
   162
slouken@617
   163
            case BODY:
slouken@617
   164
                found_BODY  = 1;
slouken@617
   165
                numsamples  = chunk_length;
slouken@617
   166
                start       = SDL_RWtell(src);
slouken@617
   167
                break;
slouken@216
   168
slouken@617
   169
            default:
slouken@617
   170
                break;
slouken@617
   171
        }
slouken@617
   172
        /* a 0 pad byte can be stored for any odd-length chunk */
slouken@617
   173
        if (chunk_length&1)
slouken@617
   174
            next_chunk++;
slouken@777
   175
    } while ((((AIFFmagic == AIFF) && (!found_SSND || !found_COMM))
slouken@777
   176
          || ((AIFFmagic == _8SVX) && (!found_VHDR || !found_BODY)))
slouken@777
   177
          && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != 1);
slouken@111
   178
slouken@777
   179
    if ((AIFFmagic == AIFF) && !found_SSND) {
slouken@617
   180
        SDL_SetError("Bad AIFF (no SSND chunk)");
slouken@617
   181
        was_error = 1;
slouken@617
   182
        goto done;
slouken@617
   183
    }
slouken@111
   184
slouken@777
   185
    if ((AIFFmagic == AIFF) && !found_COMM) {
slouken@617
   186
        SDL_SetError("Bad AIFF (no COMM chunk)");
slouken@617
   187
        was_error = 1;
slouken@617
   188
        goto done;
slouken@617
   189
    }
slouken@111
   190
slouken@777
   191
    if ((AIFFmagic == _8SVX) && !found_VHDR) {
slouken@617
   192
        SDL_SetError("Bad 8SVX (no VHDR chunk)");
slouken@617
   193
        was_error = 1;
slouken@617
   194
        goto done;
slouken@617
   195
    }
slouken@216
   196
slouken@777
   197
    if ((AIFFmagic == _8SVX) && !found_BODY) {
slouken@617
   198
        SDL_SetError("Bad 8SVX (no BODY chunk)");
slouken@617
   199
        was_error = 1;
slouken@617
   200
        goto done;
slouken@617
   201
    }
slouken@216
   202
slouken@617
   203
    /* Decode the audio data format */
slouken@621
   204
    SDL_memset(spec, 0, sizeof(*spec));
slouken@617
   205
    spec->freq = frequency;
slouken@617
   206
    switch (samplesize) {
slouken@617
   207
        case 8:
slouken@617
   208
            spec->format = AUDIO_S8;
slouken@617
   209
            break;
slouken@617
   210
        case 16:
slouken@617
   211
            spec->format = AUDIO_S16MSB;
slouken@617
   212
            break;
slouken@617
   213
        default:
slouken@617
   214
            SDL_SetError("Unsupported AIFF samplesize");
slouken@617
   215
            was_error = 1;
slouken@617
   216
            goto done;
slouken@617
   217
    }
slouken@617
   218
    spec->channels = (Uint8) channels;
slouken@617
   219
    spec->samples = 4096;       /* Good default buffer size */
slouken@111
   220
slouken@617
   221
    *audio_len = channels * numsamples * (samplesize / 8);
slouken@617
   222
    *audio_buf = (Uint8 *)SDL_malloc(*audio_len);
slouken@777
   223
    if (*audio_buf == NULL) {
slouken@617
   224
        SDL_SetError("Out of memory");
slouken@617
   225
        return(NULL);
slouken@617
   226
    }
slouken@617
   227
    SDL_RWseek(src, start, RW_SEEK_SET);
slouken@777
   228
    if (SDL_RWread(src, *audio_buf, *audio_len, 1) != 1) {
slouken@617
   229
        SDL_SetError("Unable to read audio data");
slouken@617
   230
        return(NULL);
slouken@617
   231
    }
slouken@111
   232
slouken@617
   233
    /* Don't return a buffer that isn't a multiple of samplesize */
slouken@617
   234
    *audio_len &= ~((samplesize / 8) - 1);
slouken@111
   235
slouken@111
   236
done:
slouken@625
   237
    if (freesrc && src) {
slouken@617
   238
        SDL_RWclose(src);
slouken@617
   239
    }
slouken@625
   240
    if (was_error) {
slouken@617
   241
        spec = NULL;
slouken@617
   242
    }
slouken@617
   243
    return(spec);
slouken@111
   244
}
slouken@216
   245
slouken@777
   246
/* vi: set ts=4 sw=4 expandtab: */