load_aiff.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 14 Dec 2001 12:56:55 +0000
changeset 138 4d0dc6b4985d
parent 114 83ab4ef4458b
child 140 efa15d2a5403
permissions -rw-r--r--
Updated the copyright information
     1 /*
     2     SDL_mixer:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001  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 */
    26 
    27 /* $id:$ */
    28 
    29 #include <string.h>
    30 
    31 #include "SDL_mixer.h"
    32 #include "load_aiff.h"
    33 
    34 /*********************************************/
    35 /* Define values for AIFF (IFF audio) format */
    36 /*********************************************/
    37 #define FORM		0x4d524f46		/* "FORM" */
    38 #define AIFF		0x46464941		/* "AIFF" */
    39 #define SSND		0x444e5353		/* "SSND" */
    40 #define COMM		0x4d4d4f43		/* "COMM" */
    41 
    42 /* This function was taken from libsndfile. I don't pretend to fully
    43  * understand it.
    44  */
    45 
    46 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
    47 {
    48 	/* Is the frequency outside of what we can represent with Uint32? */
    49 	if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40)
    50 		|| (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) )
    51 		return 0;
    52 
    53 	return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
    54 		| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
    55 }
    56 
    57 /* This function is based on SDL_LoadWAV_RW(). */
    58 
    59 SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc,
    60 	SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
    61 {
    62 	int was_error;
    63 	int found_SSND;
    64 	int found_COMM;
    65 	long start;
    66 
    67 	Uint32 chunk_type;
    68 	Uint32 chunk_length;
    69 	long next_chunk;
    70 
    71 	/* AIFF magic header */
    72 	Uint32 FORMchunk;
    73 	Uint32 AIFFmagic;
    74 
    75 	/* SSND chunk */
    76 	Uint32 offset;
    77 	Uint32 blocksize;
    78 
    79 	/* COMM format chunk */
    80 	Uint16 channels;
    81 	Uint32 numsamples;
    82 	Uint16 samplesize;
    83 	Uint8 sane_freq[10];
    84 	Uint32 frequency;
    85 
    86 	/* Make sure we are passed a valid data source */
    87 	was_error = 0;
    88 	if ( src == NULL ) {
    89 		was_error = 1;
    90 		goto done;
    91 	}
    92 
    93 	FORMchunk	= SDL_ReadLE32(src);
    94 	chunk_length	= SDL_ReadBE32(src);
    95 	if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */
    96 		AIFFmagic    = chunk_length;
    97 		chunk_length = FORMchunk;
    98 		FORMchunk    = FORM;
    99 	} else {
   100 		AIFFmagic    = SDL_ReadLE32(src);
   101 	}
   102 	if ( (FORMchunk != FORM) || (AIFFmagic != AIFF) ) {
   103 		SDL_SetError("Unrecognized file type (not AIFF)");
   104 		was_error = 1;
   105 		goto done;
   106 	}
   107 
   108 	/* TODO: Better santity-checking. */
   109 
   110 	found_SSND = 0;
   111 	found_COMM = 0;
   112 
   113 	do {
   114 		chunk_type	= SDL_ReadLE32(src);
   115 		chunk_length	= SDL_ReadBE32(src);
   116 		next_chunk	= SDL_RWtell(src) + chunk_length;
   117 
   118 		/* Paranoia to avoid infinite loops */
   119 		if (chunk_length == 0)
   120 			break;
   121 
   122 		switch (chunk_type) {
   123 			case SSND:
   124 				found_SSND	= 1;
   125 				offset		= SDL_ReadBE32(src);
   126 				blocksize	= SDL_ReadBE32(src);
   127 				start		= SDL_RWtell(src) + offset;
   128 				break;
   129 
   130 			case COMM:
   131 				found_COMM	= 1;
   132 				channels	= SDL_ReadBE16(src);
   133 				numsamples	= SDL_ReadBE32(src);
   134 				samplesize	= SDL_ReadBE16(src);
   135 				SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   136 				frequency	= SANE_to_Uint32(sane_freq);
   137 				if (frequency == 0) {
   138 					SDL_SetError("Bad AIFF sample frequency");
   139 					was_error = 1;
   140 					goto done;
   141 				}
   142 				break;
   143 
   144 			default:
   145 				break;
   146 		}
   147 	} while ( ( !found_SSND || !found_COMM )
   148 		  && SDL_RWseek(src, next_chunk, SEEK_SET) != 1 );
   149 
   150 	if ( !found_SSND ) {
   151 		SDL_SetError("Bad AIFF (no SSND chunk)");
   152 		was_error = 1;
   153 		goto done;
   154 	}
   155 
   156 	if ( !found_COMM ) {
   157 		SDL_SetError("Bad AIFF (no COMM chunk)");
   158 		was_error = 1;
   159 		goto done;
   160 	}
   161 
   162 	/* Decode the audio data format */
   163 	memset(spec, 0, sizeof(*spec));
   164 	spec->freq = frequency;
   165 	switch (samplesize) {
   166 		case 8:
   167 			spec->format = AUDIO_S8;
   168 			break;
   169 		case 16:
   170 			spec->format = AUDIO_S16MSB;
   171 			break;
   172 		default:
   173 			SDL_SetError("Unsupported AIFF samplesize");
   174 			was_error = 1;
   175 			goto done;
   176 	}
   177 	spec->channels = (Uint8) channels;
   178 	spec->samples = 4096;		/* Good default buffer size */
   179 
   180 	*audio_len = channels * numsamples * (samplesize / 8);
   181 	*audio_buf = (Uint8 *)malloc(*audio_len);
   182 	if ( *audio_buf == NULL ) {
   183 		SDL_SetError("Out of memory");
   184 		return(NULL);
   185 	}
   186 	SDL_RWseek(src, start, SEEK_SET);
   187 	if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) {
   188 		SDL_SetError("Unable to read audio data");
   189 		return(NULL);
   190 	}
   191 
   192 	/* Don't return a buffer that isn't a multiple of samplesize */
   193 	*audio_len &= ~((samplesize / 8) - 1);
   194 
   195 done:
   196 	if ( freesrc && src ) {
   197 		SDL_RWclose(src);
   198 	}
   199 	if ( was_error ) {
   200 		spec = NULL;
   201 	}
   202 	return(spec);
   203 }