load_aiff.c
changeset 111 3b709e102787
child 114 83ab4ef4458b
equal deleted inserted replaced
110:1885d27bab6c 111:3b709e102787
       
     1 /*
       
     2     SDL_mixer:  An audio mixer library based on the SDL library
       
     3     Copyright (C) 1997-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 #include "SDL_mutex.h"
       
    28 #include "SDL_endian.h"
       
    29 #include "SDL_timer.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 }