load_aiff.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 19 Oct 2009 01:03:40 +0000
changeset 462 028b317d01d2
parent 386 695494546b3c
child 473 60b7e1c4f6b2
permissions -rw-r--r--
Added the AudioUnit framework for the native MIDI code.
slouken@111
     1
/*
slouken@111
     2
    SDL_mixer:  An audio mixer library based on the SDL library
slouken@386
     3
    Copyright (C) 1997-2009 Sam Lantinga
slouken@111
     4
slouken@111
     5
    This library is free software; you can redistribute it and/or
slouken@111
     6
    modify it under the terms of the GNU Library General Public
slouken@111
     7
    License as published by the Free Software Foundation; either
slouken@111
     8
    version 2 of the License, or (at your option) any later version.
slouken@111
     9
slouken@111
    10
    This library is distributed in the hope that it will be useful,
slouken@111
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@111
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@111
    13
    Library General Public License for more details.
slouken@111
    14
slouken@111
    15
    You should have received a copy of the GNU Library General Public
slouken@111
    16
    License along with this library; if not, write to the Free
slouken@111
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@111
    18
slouken@111
    19
    This is the source needed to decode an AIFF file into a waveform.
slouken@111
    20
    It's pretty straightforward once you get going. The only
slouken@111
    21
    externally-callable function is Mix_LoadAIFF_RW(), which is meant to
slouken@111
    22
    act as identically to SDL_LoadWAV_RW() as possible.
slouken@111
    23
slouken@111
    24
    This file by Torbjrn Andersson (torbjorn.andersson@eurotime.se)
slouken@216
    25
    8SVX file support added by Marc Le Douarain (mavati@club-internet.fr)
slouken@216
    26
    in december 2002.
slouken@111
    27
*/
slouken@111
    28
slouken@140
    29
/* $Id$ */
slouken@138
    30
slouken@142
    31
#include <stdlib.h>
slouken@114
    32
#include <string.h>
slouken@111
    33
slouken@142
    34
#include "SDL_endian.h"
slouken@111
    35
#include "SDL_mixer.h"
slouken@111
    36
#include "load_aiff.h"
slouken@111
    37
slouken@111
    38
/*********************************************/
slouken@111
    39
/* Define values for AIFF (IFF audio) format */
slouken@111
    40
/*********************************************/
slouken@111
    41
#define FORM		0x4d524f46		/* "FORM" */
slouken@216
    42
slouken@111
    43
#define AIFF		0x46464941		/* "AIFF" */
slouken@111
    44
#define SSND		0x444e5353		/* "SSND" */
slouken@111
    45
#define COMM		0x4d4d4f43		/* "COMM" */
slouken@111
    46
slouken@216
    47
#define _8SVX		0x58565338		/* "8SVX" */
slouken@216
    48
#define VHDR		0x52444856		/* "VHDR" */
slouken@216
    49
#define BODY		0x59444F42		/* "BODY" */
slouken@216
    50
slouken@111
    51
/* This function was taken from libsndfile. I don't pretend to fully
slouken@111
    52
 * understand it.
slouken@111
    53
 */
slouken@111
    54
slouken@111
    55
static Uint32 SANE_to_Uint32 (Uint8 *sanebuf)
slouken@111
    56
{
slouken@111
    57
	/* Is the frequency outside of what we can represent with Uint32? */
slouken@111
    58
	if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40)
slouken@111
    59
		|| (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) )
slouken@111
    60
		return 0;
slouken@111
    61
slouken@111
    62
	return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7)
slouken@111
    63
		| (sanebuf[5] >> 1)) >> (29 - sanebuf[1]);
slouken@111
    64
}
slouken@111
    65
slouken@111
    66
/* This function is based on SDL_LoadWAV_RW(). */
slouken@111
    67
slouken@111
    68
SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc,
slouken@111
    69
	SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
slouken@111
    70
{
slouken@111
    71
	int was_error;
slouken@111
    72
	int found_SSND;
slouken@111
    73
	int found_COMM;
slouken@216
    74
	int found_VHDR;
slouken@216
    75
	int found_BODY;
slouken@142
    76
	long start = 0;
slouken@111
    77
slouken@111
    78
	Uint32 chunk_type;
slouken@111
    79
	Uint32 chunk_length;
slouken@111
    80
	long next_chunk;
slouken@111
    81
slouken@111
    82
	/* AIFF magic header */
slouken@111
    83
	Uint32 FORMchunk;
slouken@111
    84
	Uint32 AIFFmagic;
slouken@111
    85
slouken@111
    86
	/* SSND chunk */
slouken@111
    87
	Uint32 offset;
slouken@111
    88
	Uint32 blocksize;
slouken@111
    89
slouken@111
    90
	/* COMM format chunk */
slouken@142
    91
	Uint16 channels = 0;
slouken@142
    92
	Uint32 numsamples = 0;
slouken@142
    93
	Uint16 samplesize = 0;
slouken@111
    94
	Uint8 sane_freq[10];
slouken@142
    95
	Uint32 frequency = 0;
slouken@111
    96
slouken@111
    97
	/* Make sure we are passed a valid data source */
slouken@111
    98
	was_error = 0;
slouken@111
    99
	if ( src == NULL ) {
slouken@111
   100
		was_error = 1;
slouken@111
   101
		goto done;
slouken@111
   102
	}
slouken@111
   103
slouken@111
   104
	FORMchunk	= SDL_ReadLE32(src);
slouken@111
   105
	chunk_length	= SDL_ReadBE32(src);
slouken@111
   106
	if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */
slouken@111
   107
		AIFFmagic    = chunk_length;
slouken@111
   108
		chunk_length = FORMchunk;
slouken@111
   109
		FORMchunk    = FORM;
slouken@111
   110
	} else {
slouken@111
   111
		AIFFmagic    = SDL_ReadLE32(src);
slouken@111
   112
	}
slouken@216
   113
	if ( (FORMchunk != FORM) || ( (AIFFmagic != AIFF) && (AIFFmagic != _8SVX) ) ) {
slouken@216
   114
		SDL_SetError("Unrecognized file type (not AIFF nor 8SVX)");
slouken@111
   115
		was_error = 1;
slouken@111
   116
		goto done;
slouken@111
   117
	}
slouken@111
   118
slouken@111
   119
	/* TODO: Better santity-checking. */
slouken@111
   120
slouken@111
   121
	found_SSND = 0;
slouken@111
   122
	found_COMM = 0;
slouken@216
   123
	found_VHDR = 0;
slouken@216
   124
	found_BODY = 0;
slouken@111
   125
slouken@111
   126
	do {
slouken@111
   127
		chunk_type	= SDL_ReadLE32(src);
slouken@111
   128
		chunk_length	= SDL_ReadBE32(src);
slouken@111
   129
		next_chunk	= SDL_RWtell(src) + chunk_length;
slouken@111
   130
		/* Paranoia to avoid infinite loops */
slouken@111
   131
		if (chunk_length == 0)
slouken@111
   132
			break;
slouken@111
   133
slouken@111
   134
		switch (chunk_type) {
slouken@111
   135
			case SSND:
slouken@111
   136
				found_SSND	= 1;
slouken@111
   137
				offset		= SDL_ReadBE32(src);
slouken@111
   138
				blocksize	= SDL_ReadBE32(src);
slouken@111
   139
				start		= SDL_RWtell(src) + offset;
slouken@111
   140
				break;
slouken@111
   141
slouken@111
   142
			case COMM:
slouken@111
   143
				found_COMM	= 1;
slouken@111
   144
				channels	= SDL_ReadBE16(src);
slouken@111
   145
				numsamples	= SDL_ReadBE32(src);
slouken@111
   146
				samplesize	= SDL_ReadBE16(src);
slouken@111
   147
				SDL_RWread(src, sane_freq, sizeof(sane_freq), 1);
slouken@111
   148
				frequency	= SANE_to_Uint32(sane_freq);
slouken@111
   149
				if (frequency == 0) {
slouken@111
   150
					SDL_SetError("Bad AIFF sample frequency");
slouken@111
   151
					was_error = 1;
slouken@111
   152
					goto done;
slouken@111
   153
				}
slouken@111
   154
				break;
slouken@111
   155
slouken@216
   156
			case VHDR:
slouken@216
   157
				found_VHDR	= 1;
slouken@216
   158
				SDL_ReadBE32(src);
slouken@216
   159
				SDL_ReadBE32(src);
slouken@216
   160
				SDL_ReadBE32(src);
slouken@216
   161
				frequency = SDL_ReadBE16(src);
slouken@216
   162
				channels = 1;
slouken@216
   163
				samplesize = 8;
slouken@216
   164
				break;
slouken@216
   165
slouken@216
   166
			case BODY:
slouken@216
   167
				found_BODY	= 1;
slouken@216
   168
				numsamples	= chunk_length;
slouken@216
   169
				start		= SDL_RWtell(src);
slouken@216
   170
				break;
slouken@216
   171
slouken@111
   172
			default:
slouken@111
   173
				break;
slouken@111
   174
		}
slouken@216
   175
		/* a 0 pad byte can be stored for any odd-length chunk */
slouken@216
   176
		if (chunk_length&1)
slouken@216
   177
			next_chunk++;
slouken@216
   178
	} while ( ( ( (AIFFmagic == AIFF) && ( !found_SSND || !found_COMM ) )
slouken@216
   179
		  || ( (AIFFmagic == _8SVX ) && ( !found_VHDR || !found_BODY ) ) )
slouken@111
   180
		  && SDL_RWseek(src, next_chunk, SEEK_SET) != 1 );
slouken@111
   181
slouken@216
   182
	if ( (AIFFmagic == AIFF) && !found_SSND ) {
slouken@111
   183
		SDL_SetError("Bad AIFF (no SSND chunk)");
slouken@111
   184
		was_error = 1;
slouken@111
   185
		goto done;
slouken@111
   186
	}
slouken@111
   187
slouken@216
   188
	if ( (AIFFmagic == AIFF) && !found_COMM ) {
slouken@111
   189
		SDL_SetError("Bad AIFF (no COMM chunk)");
slouken@111
   190
		was_error = 1;
slouken@111
   191
		goto done;
slouken@111
   192
	}
slouken@111
   193
slouken@216
   194
	if ( (AIFFmagic == _8SVX) && !found_VHDR ) {
slouken@216
   195
		SDL_SetError("Bad 8SVX (no VHDR chunk)");
slouken@216
   196
		was_error = 1;
slouken@216
   197
		goto done;
slouken@216
   198
	}
slouken@216
   199
slouken@216
   200
	if ( (AIFFmagic == _8SVX) && !found_BODY ) {
slouken@216
   201
		SDL_SetError("Bad 8SVX (no BODY chunk)");
slouken@216
   202
		was_error = 1;
slouken@216
   203
		goto done;
slouken@216
   204
	}
slouken@216
   205
slouken@111
   206
	/* Decode the audio data format */
slouken@111
   207
	memset(spec, 0, sizeof(*spec));
slouken@111
   208
	spec->freq = frequency;
slouken@111
   209
	switch (samplesize) {
slouken@111
   210
		case 8:
slouken@111
   211
			spec->format = AUDIO_S8;
slouken@111
   212
			break;
slouken@111
   213
		case 16:
slouken@111
   214
			spec->format = AUDIO_S16MSB;
slouken@111
   215
			break;
slouken@111
   216
		default:
slouken@111
   217
			SDL_SetError("Unsupported AIFF samplesize");
slouken@111
   218
			was_error = 1;
slouken@111
   219
			goto done;
slouken@111
   220
	}
slouken@111
   221
	spec->channels = (Uint8) channels;
slouken@111
   222
	spec->samples = 4096;		/* Good default buffer size */
slouken@111
   223
slouken@111
   224
	*audio_len = channels * numsamples * (samplesize / 8);
slouken@111
   225
	*audio_buf = (Uint8 *)malloc(*audio_len);
slouken@111
   226
	if ( *audio_buf == NULL ) {
slouken@111
   227
		SDL_SetError("Out of memory");
slouken@111
   228
		return(NULL);
slouken@111
   229
	}
slouken@111
   230
	SDL_RWseek(src, start, SEEK_SET);
slouken@111
   231
	if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) {
slouken@111
   232
		SDL_SetError("Unable to read audio data");
slouken@111
   233
		return(NULL);
slouken@111
   234
	}
slouken@111
   235
slouken@111
   236
	/* Don't return a buffer that isn't a multiple of samplesize */
slouken@111
   237
	*audio_len &= ~((samplesize / 8) - 1);
slouken@111
   238
slouken@111
   239
done:
slouken@111
   240
	if ( freesrc && src ) {
slouken@111
   241
		SDL_RWclose(src);
slouken@111
   242
	}
slouken@111
   243
	if ( was_error ) {
slouken@111
   244
		spec = NULL;
slouken@111
   245
	}
slouken@111
   246
	return(spec);
slouken@111
   247
}
slouken@216
   248