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