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