load_aiff.c
author Ozkan Sezer <sezeroz@gmail.com>
Sat, 13 Oct 2018 23:02:04 +0300
branchSDL-1.2
changeset 908 6b860486ce24
parent 561 87bdb4c81c0b
child 601 05123263dab3
permissions -rw-r--r--
Mix_InitMP3: unload dll if mpg123_init() fails.
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