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