load_aiff.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 01 Jan 2017 18:50:09 -0800
changeset 725 bdf7b8d20566
parent 711 f40c5ac95b12
child 777 92882ef2ab81
permissions -rw-r--r--
Updated copyright for 2017
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 
    21   This is the source needed to decode an AIFF file into a waveform.
    22   It's pretty straightforward once you get going. The only
    23   externally-callable function is Mix_LoadAIFF_RW(), which is meant to
    24   act as identically to SDL_LoadWAV_RW() as possible.
    25 
    26   This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se)
    27   8SVX file support added by Marc Le Douarain (mavati@club-internet.fr)
    28   in december 2002.
    29 */
    30 
    31 /* $Id$ */
    32 
    33 #include <stdlib.h>
    34 #include <string.h>
    35 
    36 #include "SDL_endian.h"
    37 #include "SDL_mixer.h"
    38 #include "load_aiff.h"
    39 
    40 /*********************************************/
    41 /* Define values for AIFF (IFF audio) format */
    42 /*********************************************/
    43 #define FORM        0x4d524f46      /* "FORM" */
    44 
    45 #define AIFF        0x46464941      /* "AIFF" */
    46 #define SSND        0x444e5353      /* "SSND" */
    47 #define COMM        0x4d4d4f43      /* "COMM" */
    48 
    49 #define _8SVX       0x58565338      /* "8SVX" */
    50 #define VHDR        0x52444856      /* "VHDR" */
    51 #define BODY        0x59444F42      /* "BODY" */
    52 
    53 /* This function was taken from libsndfile. I don't pretend to fully
    54  * understand it.
    55  */
    56 
    57 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
    58 {
    59     /* Is the frequency outside of what we can represent with Uint32? */
    60     if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40)
    61         || (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) )
    62         return 0;
    63 
    64     return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
    65         | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
    66 }
    67 
    68 /* This function is based on SDL_LoadWAV_RW(). */
    69 
    70 SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc,
    71     SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
    72 {
    73     int was_error;
    74     int found_SSND;
    75     int found_COMM;
    76     int found_VHDR;
    77     int found_BODY;
    78     Sint64 start = 0;
    79 
    80     Uint32 chunk_type;
    81     Uint32 chunk_length;
    82     Sint64 next_chunk;
    83 
    84     /* AIFF magic header */
    85     Uint32 FORMchunk;
    86     Uint32 AIFFmagic;
    87 
    88     /* SSND chunk */
    89     Uint32 offset;
    90     Uint32 blocksize;
    91 
    92     /* COMM format chunk */
    93     Uint16 channels = 0;
    94     Uint32 numsamples = 0;
    95     Uint16 samplesize = 0;
    96     Uint8 sane_freq[10];
    97     Uint32 frequency = 0;
    98 
    99     /* Make sure we are passed a valid data source */
   100     was_error = 0;
   101     if ( src == NULL ) {
   102         was_error = 1;
   103         goto done;
   104     }
   105 
   106     FORMchunk   = SDL_ReadLE32(src);
   107     chunk_length    = SDL_ReadBE32(src);
   108     if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */
   109         AIFFmagic    = chunk_length;
   110         chunk_length = FORMchunk;
   111         FORMchunk    = FORM;
   112     } else {
   113         AIFFmagic    = SDL_ReadLE32(src);
   114     }
   115     if ( (FORMchunk != FORM) || ( (AIFFmagic != AIFF) && (AIFFmagic != _8SVX) ) ) {
   116         SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)");
   117         was_error = 1;
   118         goto done;
   119     }
   120 
   121     /* TODO: Better santity-checking. */
   122 
   123     found_SSND = 0;
   124     found_COMM = 0;
   125     found_VHDR = 0;
   126     found_BODY = 0;
   127 
   128     do {
   129         chunk_type  = SDL_ReadLE32(src);
   130         chunk_length = SDL_ReadBE32(src);
   131         next_chunk  = SDL_RWtell(src) + chunk_length;
   132         /* Paranoia to avoid infinite loops */
   133         if (chunk_length == 0)
   134             break;
   135 
   136         switch (chunk_type) {
   137             case SSND:
   138                 found_SSND  = 1;
   139                 offset      = SDL_ReadBE32(src);
   140                 blocksize   = SDL_ReadBE32(src);
   141                 start       = SDL_RWtell(src) + offset;
   142                 break;
   143 
   144             case COMM:
   145                 found_COMM  = 1;
   146                 channels    = SDL_ReadBE16(src);
   147                 numsamples  = SDL_ReadBE32(src);
   148                 samplesize  = SDL_ReadBE16(src);
   149                 SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   150                 frequency   = SANE_to_Uint32(sane_freq);
   151                 if (frequency == 0) {
   152                     SDL_SetError("Bad AIFF sample frequency");
   153                     was_error = 1;
   154                     goto done;
   155                 }
   156                 break;
   157 
   158             case VHDR:
   159                 found_VHDR  = 1;
   160                 SDL_ReadBE32(src);
   161                 SDL_ReadBE32(src);
   162                 SDL_ReadBE32(src);
   163                 frequency = SDL_ReadBE16(src);
   164                 channels = 1;
   165                 samplesize = 8;
   166                 break;
   167 
   168             case BODY:
   169                 found_BODY  = 1;
   170                 numsamples  = chunk_length;
   171                 start       = SDL_RWtell(src);
   172                 break;
   173 
   174             default:
   175                 break;
   176         }
   177         /* a 0 pad byte can be stored for any odd-length chunk */
   178         if (chunk_length&1)
   179             next_chunk++;
   180     } while ( ( ( (AIFFmagic == AIFF) && ( !found_SSND || !found_COMM ) )
   181           || ( (AIFFmagic == _8SVX ) && ( !found_VHDR || !found_BODY ) ) )
   182           && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != 1 );
   183 
   184     if ( (AIFFmagic == AIFF) && !found_SSND ) {
   185         SDL_SetError("Bad AIFF (no SSND chunk)");
   186         was_error = 1;
   187         goto done;
   188     }
   189 
   190     if ( (AIFFmagic == AIFF) && !found_COMM ) {
   191         SDL_SetError("Bad AIFF (no COMM chunk)");
   192         was_error = 1;
   193         goto done;
   194     }
   195 
   196     if ( (AIFFmagic == _8SVX) && !found_VHDR ) {
   197         SDL_SetError("Bad 8SVX (no VHDR chunk)");
   198         was_error = 1;
   199         goto done;
   200     }
   201 
   202     if ( (AIFFmagic == _8SVX) && !found_BODY ) {
   203         SDL_SetError("Bad 8SVX (no BODY chunk)");
   204         was_error = 1;
   205         goto done;
   206     }
   207 
   208     /* Decode the audio data format */
   209     SDL_memset(spec, 0, sizeof(*spec));
   210     spec->freq = frequency;
   211     switch (samplesize) {
   212         case 8:
   213             spec->format = AUDIO_S8;
   214             break;
   215         case 16:
   216             spec->format = AUDIO_S16MSB;
   217             break;
   218         default:
   219             SDL_SetError("Unsupported AIFF samplesize");
   220             was_error = 1;
   221             goto done;
   222     }
   223     spec->channels = (Uint8) channels;
   224     spec->samples = 4096;       /* Good default buffer size */
   225 
   226     *audio_len = channels * numsamples * (samplesize / 8);
   227     *audio_buf = (Uint8 *)SDL_malloc(*audio_len);
   228     if ( *audio_buf == NULL ) {
   229         SDL_SetError("Out of memory");
   230         return(NULL);
   231     }
   232     SDL_RWseek(src, start, RW_SEEK_SET);
   233     if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) {
   234         SDL_SetError("Unable to read audio data");
   235         return(NULL);
   236     }
   237 
   238     /* Don't return a buffer that isn't a multiple of samplesize */
   239     *audio_len &= ~((samplesize / 8) - 1);
   240 
   241 done:
   242     if (freesrc && src) {
   243         SDL_RWclose(src);
   244     }
   245     if (was_error) {
   246         spec = NULL;
   247     }
   248     return(spec);
   249 }
   250