load_aiff.c
author Ozkan Sezer <sezeroz@gmail.com>
Sun, 07 Oct 2018 03:07:33 +0300
branchSDL-1.2
changeset 885 dd251a0ec666
parent 561 87bdb4c81c0b
child 601 05123263dab3
permissions -rw-r--r--
backport fix for bug #2795. (from 2.0 branch commit b28b41b93ba7).
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 
    21   This is the source needed to decode an AIFF file into a waveform.
    22   It's pretty straightforward once you get going. The only
    23   externally-callable function is Mix_LoadAIFF_RW(), which is meant to
    24   act as identically to SDL_LoadWAV_RW() as possible.
    25 
    26   This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se)
    27   8SVX file support added by Marc Le Douarain (mavati@club-internet.fr)
    28   in december 2002.
    29 */
    30 
    31 /* $Id$ */
    32 
    33 #include <stdlib.h>
    34 #include <string.h>
    35 
    36 #include "SDL_endian.h"
    37 #include "SDL_mixer.h"
    38 #include "load_aiff.h"
    39 
    40 /*********************************************/
    41 /* Define values for AIFF (IFF audio) format */
    42 /*********************************************/
    43 #define FORM		0x4d524f46		/* "FORM" */
    44 
    45 #define AIFF		0x46464941		/* "AIFF" */
    46 #define SSND		0x444e5353		/* "SSND" */
    47 #define COMM		0x4d4d4f43		/* "COMM" */
    48 
    49 #define _8SVX		0x58565338		/* "8SVX" */
    50 #define VHDR		0x52444856		/* "VHDR" */
    51 #define BODY		0x59444F42		/* "BODY" */
    52 
    53 /* This function was taken from libsndfile. I don't pretend to fully
    54  * understand it.
    55  */
    56 
    57 static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
    58 {
    59 	/* Is the frequency outside of what we can represent with Uint32? */
    60 	if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40)
    61 		|| (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) )
    62 		return 0;
    63 
    64 	return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
    65 		| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
    66 }
    67 
    68 /* This function is based on SDL_LoadWAV_RW(). */
    69 
    70 SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc,
    71 	SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
    72 {
    73 	int was_error;
    74 	int found_SSND;
    75 	int found_COMM;
    76 	int found_VHDR;
    77 	int found_BODY;
    78 	long start = 0;
    79 
    80 	Uint32 chunk_type;
    81 	Uint32 chunk_length;
    82 	long next_chunk;
    83 
    84 	/* AIFF magic header */
    85 	Uint32 FORMchunk;
    86 	Uint32 AIFFmagic;
    87 
    88 	/* SSND chunk */
    89 	Uint32 offset;
    90 	Uint32 blocksize;
    91 
    92 	/* COMM format chunk */
    93 	Uint16 channels = 0;
    94 	Uint32 numsamples = 0;
    95 	Uint16 samplesize = 0;
    96 	Uint8 sane_freq[10];
    97 	Uint32 frequency = 0;
    98 
    99 	/* Make sure we are passed a valid data source */
   100 	was_error = 0;
   101 	if ( src == NULL ) {
   102 		was_error = 1;
   103 		goto done;
   104 	}
   105 
   106 	FORMchunk	= SDL_ReadLE32(src);
   107 	chunk_length	= SDL_ReadBE32(src);
   108 	if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */
   109 		AIFFmagic    = chunk_length;
   110 		chunk_length = FORMchunk;
   111 		FORMchunk    = FORM;
   112 	} else {
   113 		AIFFmagic    = SDL_ReadLE32(src);
   114 	}
   115 	if ( (FORMchunk != FORM) || ( (AIFFmagic != AIFF) && (AIFFmagic != _8SVX) ) ) {
   116 		SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)");
   117 		was_error = 1;
   118 		goto done;
   119 	}
   120 
   121 	/* TODO: Better santity-checking. */
   122 
   123 	found_SSND = 0;
   124 	found_COMM = 0;
   125 	found_VHDR = 0;
   126 	found_BODY = 0;
   127 
   128 	do {
   129 		chunk_type	= SDL_ReadLE32(src);
   130 		chunk_length	= SDL_ReadBE32(src);
   131 		next_chunk	= SDL_RWtell(src) + chunk_length;
   132 		/* Paranoia to avoid infinite loops */
   133 		if (chunk_length == 0)
   134 			break;
   135 
   136 		switch (chunk_type) {
   137 			case SSND:
   138 				found_SSND	= 1;
   139 				offset		= SDL_ReadBE32(src);
   140 				blocksize	= SDL_ReadBE32(src);
   141 				start		= SDL_RWtell(src) + offset;
   142 				break;
   143 
   144 			case COMM:
   145 				found_COMM	= 1;
   146 				channels	= SDL_ReadBE16(src);
   147 				numsamples	= SDL_ReadBE32(src);
   148 				samplesize	= SDL_ReadBE16(src);
   149 				SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
   150 				frequency	= SANE_to_Uint32(sane_freq);
   151 				if (frequency == 0) {
   152 					SDL_SetError("Bad AIFF sample frequency");
   153 					was_error = 1;
   154 					goto done;
   155 				}
   156 				break;
   157 
   158 			case VHDR:
   159 				found_VHDR	= 1;
   160 				SDL_ReadBE32(src);
   161 				SDL_ReadBE32(src);
   162 				SDL_ReadBE32(src);
   163 				frequency = SDL_ReadBE16(src);
   164 				channels = 1;
   165 				samplesize = 8;
   166 				break;
   167 
   168 			case BODY:
   169 				found_BODY	= 1;
   170 				numsamples	= chunk_length;
   171 				start		= SDL_RWtell(src);
   172 				break;
   173 
   174 			default:
   175 				break;
   176 		}
   177 		/* a 0 pad byte can be stored for any odd-length chunk */
   178 		if (chunk_length&1)
   179 			next_chunk++;
   180 	} while ( ( ( (AIFFmagic == AIFF) && ( !found_SSND || !found_COMM ) )
   181 		  || ( (AIFFmagic == _8SVX ) && ( !found_VHDR || !found_BODY ) ) )
   182 		  && SDL_RWseek(src, next_chunk, RW_SEEK_SET) != 1 );
   183 
   184 	if ( (AIFFmagic == AIFF) && !found_SSND ) {
   185 		SDL_SetError("Bad AIFF (no SSND chunk)");
   186 		was_error = 1;
   187 		goto done;
   188 	}
   189 
   190 	if ( (AIFFmagic == AIFF) && !found_COMM ) {
   191 		SDL_SetError("Bad AIFF (no COMM chunk)");
   192 		was_error = 1;
   193 		goto done;
   194 	}
   195 
   196 	if ( (AIFFmagic == _8SVX) && !found_VHDR ) {
   197 		SDL_SetError("Bad 8SVX (no VHDR chunk)");
   198 		was_error = 1;
   199 		goto done;
   200 	}
   201 
   202 	if ( (AIFFmagic == _8SVX) && !found_BODY ) {
   203 		SDL_SetError("Bad 8SVX (no BODY chunk)");
   204 		was_error = 1;
   205 		goto done;
   206 	}
   207 
   208 	/* Decode the audio data format */
   209 	memset(spec, 0, sizeof(*spec));
   210 	spec->freq = frequency;
   211 	switch (samplesize) {
   212 		case 8:
   213 			spec->format = AUDIO_S8;
   214 			break;
   215 		case 16:
   216 			spec->format = AUDIO_S16MSB;
   217 			break;
   218 		default:
   219 			SDL_SetError("Unsupported AIFF samplesize");
   220 			was_error = 1;
   221 			goto done;
   222 	}
   223 	spec->channels = (Uint8) channels;
   224 	spec->samples = 4096;		/* Good default buffer size */
   225 
   226 	*audio_len = channels * numsamples * (samplesize / 8);
   227 	*audio_buf = (Uint8 *)SDL_malloc(*audio_len);
   228 	if ( *audio_buf == NULL ) {
   229 		SDL_SetError("Out of memory");
   230 		return(NULL);
   231 	}
   232 	SDL_RWseek(src, start, RW_SEEK_SET);
   233 	if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) {
   234 		SDL_SetError("Unable to read audio data");
   235 		return(NULL);
   236 	}
   237 
   238 	/* Don't return a buffer that isn't a multiple of samplesize */
   239 	*audio_len &= ~((samplesize / 8) - 1);
   240 
   241 done:
   242 	if ( freesrc && src ) {
   243 		SDL_RWclose(src);
   244 	}
   245 	if ( was_error ) {
   246 		spec = NULL;
   247 	}
   248 	return(spec);
   249 }
   250