load_aiff.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 03 Oct 2009 20:43:33 +0000
changeset 419 e27fe0bfe470
parent 386 695494546b3c
child 473 60b7e1c4f6b2
permissions -rw-r--r--
Sam Lantinga - Sat Oct 3 13:33:36 PDT 2009
* MOD support uses libmikmod and is dynamically loaded by default
     1 /*
     2     SDL_mixer:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     This is the source needed to decode an AIFF file into a waveform.
    20     It's pretty straightforward once you get going. The only
    21     externally-callable function is Mix_LoadAIFF_RW(), which is meant to
    22     act as identically to SDL_LoadWAV_RW() as possible.
    23 
    24     This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se)
    25     8SVX file support added by Marc Le Douarain (mavati@club-internet.fr)
    26     in december 2002.
    27 */
    28 
    29 /* $Id$ */
    30 
    31 #include <stdlib.h>
    32 #include <string.h>
    33 
    34 #include "SDL_endian.h"
    35 #include "SDL_mixer.h"
    36 #include "load_aiff.h"
    37 
    38 /*********************************************/
    39 /* Define values for AIFF (IFF audio) format */
    40 /*********************************************/
    41 #define FORM		0x4d524f46		/* "FORM" */
    42 
    43 #define AIFF		0x46464941		/* "AIFF" */
    44 #define SSND		0x444e5353		/* "SSND" */
    45 #define COMM		0x4d4d4f43		/* "COMM" */
    46 
    47 #define _8SVX		0x58565338		/* "8SVX" */
    48 #define VHDR		0x52444856		/* "VHDR" */
    49 #define BODY		0x59444F42		/* "BODY" */
    50 
    51 /* This function was taken from libsndfile. I don't pretend to fully
    52  * understand it.
    53  */
    54 
    55 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
    56 {
    57 	/* Is the frequency outside of what we can represent with Uint32? */
    58 	if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40)
    59 		|| (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) )
    60 		return 0;
    61 
    62 	return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
    63 		| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
    64 }
    65 
    66 /* This function is based on SDL_LoadWAV_RW(). */
    67 
    68 SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc,
    69 	SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
    70 {
    71 	int was_error;
    72 	int found_SSND;
    73 	int found_COMM;
    74 	int found_VHDR;
    75 	int found_BODY;
    76 	long start = 0;
    77 
    78 	Uint32 chunk_type;
    79 	Uint32 chunk_length;
    80 	long next_chunk;
    81 
    82 	/* AIFF magic header */
    83 	Uint32 FORMchunk;
    84 	Uint32 AIFFmagic;
    85 
    86 	/* SSND chunk */
    87 	Uint32 offset;
    88 	Uint32 blocksize;
    89 
    90 	/* COMM format chunk */
    91 	Uint16 channels = 0;
    92 	Uint32 numsamples = 0;
    93 	Uint16 samplesize = 0;
    94 	Uint8 sane_freq[10];
    95 	Uint32 frequency = 0;
    96 
    97 	/* Make sure we are passed a valid data source */
    98 	was_error = 0;
    99 	if ( src == NULL ) {
   100 		was_error = 1;
   101 		goto done;
   102 	}
   103 
   104 	FORMchunk	= SDL_ReadLE32(src);
   105 	chunk_length	= SDL_ReadBE32(src);
   106 	if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */
   107 		AIFFmagic    = chunk_length;
   108 		chunk_length = FORMchunk;
   109 		FORMchunk    = FORM;
   110 	} else {
   111 		AIFFmagic    = SDL_ReadLE32(src);
   112 	}
   113 	if ( (FORMchunk != FORM) || ( (AIFFmagic != AIFF) && (AIFFmagic != _8SVX) ) ) {
   114 		SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)");
   115 		was_error = 1;
   116 		goto done;
   117 	}
   118 
   119 	/* TODO: Better santity-checking. */
   120 
   121 	found_SSND = 0;
   122 	found_COMM = 0;
   123 	found_VHDR = 0;
   124 	found_BODY = 0;
   125 
   126 	do {
   127 		chunk_type	= SDL_ReadLE32(src);
   128 		chunk_length	= SDL_ReadBE32(src);
   129 		next_chunk	= SDL_RWtell(src) + chunk_length;
   130 		/* Paranoia to avoid infinite loops */
   131 		if (chunk_length == 0)
   132 			break;
   133 
   134 		switch (chunk_type) {
   135 			case SSND:
   136 				found_SSND	= 1;
   137 				offset		= SDL_ReadBE32(src);
   138 				blocksize	= SDL_ReadBE32(src);
   139 				start		= SDL_RWtell(src) + offset;
   140 				break;
   141 
   142 			case COMM:
   143 				found_COMM	= 1;
   144 				channels	= SDL_ReadBE16(src);
   145 				numsamples	= SDL_ReadBE32(src);
   146 				samplesize	= SDL_ReadBE16(src);
   147 				SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   148 				frequency	= SANE_to_Uint32(sane_freq);
   149 				if (frequency == 0) {
   150 					SDL_SetError("Bad AIFF sample frequency");
   151 					was_error = 1;
   152 					goto done;
   153 				}
   154 				break;
   155 
   156 			case VHDR:
   157 				found_VHDR	= 1;
   158 				SDL_ReadBE32(src);
   159 				SDL_ReadBE32(src);
   160 				SDL_ReadBE32(src);
   161 				frequency = SDL_ReadBE16(src);
   162 				channels = 1;
   163 				samplesize = 8;
   164 				break;
   165 
   166 			case BODY:
   167 				found_BODY	= 1;
   168 				numsamples	= chunk_length;
   169 				start		= SDL_RWtell(src);
   170 				break;
   171 
   172 			default:
   173 				break;
   174 		}
   175 		/* a 0 pad byte can be stored for any odd-length chunk */
   176 		if (chunk_length&1)
   177 			next_chunk++;
   178 	} while ( ( ( (AIFFmagic == AIFF) && ( !found_SSND || !found_COMM ) )
   179 		  || ( (AIFFmagic == _8SVX ) && ( !found_VHDR || !found_BODY ) ) )
   180 		  && SDL_RWseek(src, next_chunk, SEEK_SET) != 1 );
   181 
   182 	if ( (AIFFmagic == AIFF) && !found_SSND ) {
   183 		SDL_SetError("Bad AIFF (no SSND chunk)");
   184 		was_error = 1;
   185 		goto done;
   186 	}
   187 
   188 	if ( (AIFFmagic == AIFF) && !found_COMM ) {
   189 		SDL_SetError("Bad AIFF (no COMM chunk)");
   190 		was_error = 1;
   191 		goto done;
   192 	}
   193 
   194 	if ( (AIFFmagic == _8SVX) && !found_VHDR ) {
   195 		SDL_SetError("Bad 8SVX (no VHDR chunk)");
   196 		was_error = 1;
   197 		goto done;
   198 	}
   199 
   200 	if ( (AIFFmagic == _8SVX) && !found_BODY ) {
   201 		SDL_SetError("Bad 8SVX (no BODY chunk)");
   202 		was_error = 1;
   203 		goto done;
   204 	}
   205 
   206 	/* Decode the audio data format */
   207 	memset(spec, 0, sizeof(*spec));
   208 	spec->freq = frequency;
   209 	switch (samplesize) {
   210 		case 8:
   211 			spec->format = AUDIO_S8;
   212 			break;
   213 		case 16:
   214 			spec->format = AUDIO_S16MSB;
   215 			break;
   216 		default:
   217 			SDL_SetError("Unsupported AIFF samplesize");
   218 			was_error = 1;
   219 			goto done;
   220 	}
   221 	spec->channels = (Uint8) channels;
   222 	spec->samples = 4096;		/* Good default buffer size */
   223 
   224 	*audio_len = channels * numsamples * (samplesize / 8);
   225 	*audio_buf = (Uint8 *)malloc(*audio_len);
   226 	if ( *audio_buf == NULL ) {
   227 		SDL_SetError("Out of memory");
   228 		return(NULL);
   229 	}
   230 	SDL_RWseek(src, start, SEEK_SET);
   231 	if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) {
   232 		SDL_SetError("Unable to read audio data");
   233 		return(NULL);
   234 	}
   235 
   236 	/* Don't return a buffer that isn't a multiple of samplesize */
   237 	*audio_len &= ~((samplesize / 8) - 1);
   238 
   239 done:
   240 	if ( freesrc && src ) {
   241 		SDL_RWclose(src);
   242 	}
   243 	if ( was_error ) {
   244 		spec = NULL;
   245 	}
   246 	return(spec);
   247 }
   248