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