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