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