From dd972d191f4af39b64e30fee6a4e49befb1e5b2b Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Tue, 11 Sep 2001 18:24:38 +0000 Subject: [PATCH] Torbj?rn Andersson - Tue Sep 11 11:22:29 PDT 2001 * Added support for loading AIFF audio chunks --- CHANGES | 2 + Makefile.am | 8 +- load_aiff.c | 203 ++++++++++++++++++++++++++++++++++++++++++++ load_aiff.h | 31 +++++++ voc.c => load_voc.c | 15 +++- load_voc.h | 34 ++++++++ mixer.c | 48 ++++++----- wave.h | 51 ----------- wavestream.c | 53 +++++++++++- 9 files changed, 368 insertions(+), 77 deletions(-) create mode 100644 load_aiff.c create mode 100644 load_aiff.h rename voc.c => load_voc.c (97%) create mode 100644 load_voc.h delete mode 100644 wave.h diff --git a/CHANGES b/CHANGES index c4ed348b..9de831c5 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,7 @@ 1.2.1: +Torbjörn Andersson - Tue Sep 11 11:22:29 PDT 2001 + * Added support for loading AIFF audio chunks Max Horn - Tue Sep 4 20:38:11 PDT 2001 * Added native MIDI music support on MacOS and MacOS X Florian Schulze - Sun Aug 19 14:55:37 PDT 2001 diff --git a/Makefile.am b/Makefile.am index 5ed2f3f2..4cf33ade 100644 --- a/Makefile.am +++ b/Makefile.am @@ -10,16 +10,18 @@ libSDL_mixerinclude_HEADERS = \ SDL_mixer.h libSDL_mixer_la_SOURCES = \ + load_aiff.c \ + load_aiff.h \ + load_voc.c \ + load_voc.h \ mixer.c \ music.c \ music_cmd.c \ music_cmd.h \ music_ogg.c \ music_ogg.h \ - wave.h \ wavestream.c \ - wavestream.h \ - voc.c + wavestream.h if USE_MIKMOD MIKMOD_LIB = mikmod/libmikmod.la diff --git a/load_aiff.c b/load_aiff.c new file mode 100644 index 00000000..26d20d24 --- /dev/null +++ b/load_aiff.c @@ -0,0 +1,203 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This is the source needed to decode an AIFF file into a waveform. + It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadAIFF_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) +*/ + +#include "SDL_mutex.h" +#include "SDL_endian.h" +#include "SDL_timer.h" + +#include "SDL_mixer.h" +#include "load_aiff.h" + +/*********************************************/ +/* Define values for AIFF (IFF audio) format */ +/*********************************************/ +#define FORM 0x4d524f46 /* "FORM" */ +#define AIFF 0x46464941 /* "AIFF" */ +#define SSND 0x444e5353 /* "SSND" */ +#define COMM 0x4d4d4f43 /* "COMM" */ + +/* This function was taken from libsndfile. I don't pretend to fully + * understand it. + */ + +static Uint32 SANE_to_Uint32 (Uint8 *sanebuf) +{ + /* Is the frequency outside of what we can represent with Uint32? */ + if ( (sanebuf[0] & 0x80) || (sanebuf[0] <= 0x3F) || (sanebuf[0] > 0x40) + || (sanebuf[0] == 0x40 && sanebuf[1] > 0x1C) ) + return 0; + + return ((sanebuf[2] << 23) | (sanebuf[3] << 15) | (sanebuf[4] << 7) + | (sanebuf[5] >> 1)) >> (29 - sanebuf[1]); +} + +/* This function is based on SDL_LoadWAV_RW(). */ + +SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + int was_error; + int found_SSND; + int found_COMM; + long start; + + Uint32 chunk_type; + Uint32 chunk_length; + long next_chunk; + + /* AIFF magic header */ + Uint32 FORMchunk; + Uint32 AIFFmagic; + + /* SSND chunk */ + Uint32 offset; + Uint32 blocksize; + + /* COMM format chunk */ + Uint16 channels; + Uint32 numsamples; + Uint16 samplesize; + Uint8 sane_freq[10]; + Uint32 frequency; + + /* Make sure we are passed a valid data source */ + was_error = 0; + if ( src == NULL ) { + was_error = 1; + goto done; + } + + FORMchunk = SDL_ReadLE32(src); + chunk_length = SDL_ReadBE32(src); + if ( chunk_length == AIFF ) { /* The FORMchunk has already been read */ + AIFFmagic = chunk_length; + chunk_length = FORMchunk; + FORMchunk = FORM; + } else { + AIFFmagic = SDL_ReadLE32(src); + } + if ( (FORMchunk != FORM) || (AIFFmagic != AIFF) ) { + SDL_SetError("Unrecognized file type (not AIFF)"); + was_error = 1; + goto done; + } + + /* TODO: Better santity-checking. */ + + found_SSND = 0; + found_COMM = 0; + + do { + chunk_type = SDL_ReadLE32(src); + chunk_length = SDL_ReadBE32(src); + next_chunk = SDL_RWtell(src) + chunk_length; + + /* Paranoia to avoid infinite loops */ + if (chunk_length == 0) + break; + + switch (chunk_type) { + case SSND: + found_SSND = 1; + offset = SDL_ReadBE32(src); + blocksize = SDL_ReadBE32(src); + start = SDL_RWtell(src) + offset; + break; + + case COMM: + found_COMM = 1; + channels = SDL_ReadBE16(src); + numsamples = SDL_ReadBE32(src); + samplesize = SDL_ReadBE16(src); + SDL_RWread(src, sane_freq, sizeof(sane_freq), 1); + frequency = SANE_to_Uint32(sane_freq); + if (frequency == 0) { + SDL_SetError("Bad AIFF sample frequency"); + was_error = 1; + goto done; + } + break; + + default: + break; + } + } while ( ( !found_SSND || !found_COMM ) + && SDL_RWseek(src, next_chunk, SEEK_SET) != 1 ); + + if ( !found_SSND ) { + SDL_SetError("Bad AIFF (no SSND chunk)"); + was_error = 1; + goto done; + } + + if ( !found_COMM ) { + SDL_SetError("Bad AIFF (no COMM chunk)"); + was_error = 1; + goto done; + } + + /* Decode the audio data format */ + memset(spec, 0, sizeof(*spec)); + spec->freq = frequency; + switch (samplesize) { + case 8: + spec->format = AUDIO_S8; + break; + case 16: + spec->format = AUDIO_S16MSB; + break; + default: + SDL_SetError("Unsupported AIFF samplesize"); + was_error = 1; + goto done; + } + spec->channels = (Uint8) channels; + spec->samples = 4096; /* Good default buffer size */ + + *audio_len = channels * numsamples * (samplesize / 8); + *audio_buf = (Uint8 *)malloc(*audio_len); + if ( *audio_buf == NULL ) { + SDL_SetError("Out of memory"); + return(NULL); + } + SDL_RWseek(src, start, SEEK_SET); + if ( SDL_RWread(src, *audio_buf, *audio_len, 1) != 1 ) { + SDL_SetError("Unable to read audio data"); + return(NULL); + } + + /* Don't return a buffer that isn't a multiple of samplesize */ + *audio_len &= ~((samplesize / 8) - 1); + +done: + if ( freesrc && src ) { + SDL_RWclose(src); + } + if ( was_error ) { + spec = NULL; + } + return(spec); +} diff --git a/load_aiff.h b/load_aiff.h new file mode 100644 index 00000000..4404d619 --- /dev/null +++ b/load_aiff.h @@ -0,0 +1,31 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This is the source needed to decode an AIFF file into a waveform. + It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadAIFF_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Torbjörn Andersson (torbjorn.andersson@eurotime.se) +*/ + +/* $Id$ */ + +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadAIFF_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); diff --git a/voc.c b/load_voc.c similarity index 97% rename from voc.c rename to load_voc.c index 84362f22..acfbb983 100644 --- a/voc.c +++ b/load_voc.c @@ -1,6 +1,6 @@ /* SDL_mixer: An audio mixer library based on the SDL library - Copyright (C) 1997-1999 Sam Lantinga + Copyright (C) 1997-2001 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -38,6 +38,7 @@ #include "SDL_timer.h" #include "SDL_mixer.h" +#include "load_voc.h" #ifdef VOC_SAMPLES @@ -457,7 +458,15 @@ SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, return(spec); } /* Mix_LoadVOC_RW */ -#endif /* VOC_SAMPLES */ +#else -/* end of voc.c ... */ +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len) +{ + SDL_SetError("VOC file loading not supported"); + return(NULL); +} + +#endif /* VOC_SAMPLES */ +/* end of load_voc.c ... */ diff --git a/load_voc.h b/load_voc.h new file mode 100644 index 00000000..028a0ead --- /dev/null +++ b/load_voc.h @@ -0,0 +1,34 @@ +/* + SDL_mixer: An audio mixer library based on the SDL library + Copyright (C) 1997-2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + This is the source needed to decode a Creative Labs VOC file into a + waveform. It's pretty straightforward once you get going. The only + externally-callable function is Mix_LoadVOC_RW(), which is meant to + act as identically to SDL_LoadWAV_RW() as possible. + + This file by Ryan C. Gordon (icculus@linuxgames.com). + + Heavily borrowed from sox v12.17.1's voc.c. + (http://www.freshmeat.net/projects/sox/) +*/ + +/* $Id$ */ + +/* Don't call this directly; use Mix_LoadWAV_RW() for now. */ +SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, + SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); diff --git a/mixer.c b/mixer.c index e422c29a..fe1c92c8 100644 --- a/mixer.c +++ b/mixer.c @@ -33,6 +33,12 @@ #include "SDL_timer.h" #include "SDL_mixer.h" +#include "load_aiff.h" +#include "load_voc.h" + +/* Magic numbers for various audio file formats */ +#define WAVE 0x45564157 /* "WAVE" */ +#define FORM 0x4d524f46 /* "FORM" */ static int audio_opened = 0; @@ -284,22 +290,18 @@ int Mix_QuerySpec(int *frequency, Uint16 *format, int *channels) * generic setup, then call the correct file format loader. */ -#ifdef VOC_SAMPLES -SDL_AudioSpec *Mix_LoadVOC_RW (SDL_RWops *src, int freesrc, - SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len); -#endif - /* Load a wave file */ Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc) { + Uint32 magic; Mix_Chunk *chunk; - SDL_AudioSpec wavespec; + SDL_AudioSpec wavespec, *loaded; SDL_AudioCVT wavecvt; int samplesize; /* rcg06012001 Make sure src is valid */ if ( ! src ) { - SDL_SetError("Mix_LoadWAV_RW with NULL SDL_RWops"); + SDL_SetError("Mix_LoadWAV_RW with NULL src"); return(NULL); } @@ -322,22 +324,30 @@ Mix_Chunk *Mix_LoadWAV_RW(SDL_RWops *src, int freesrc) return(NULL); } - /* rcg06012001 Handle Creative Labs .VOC format chunks. */ -#ifdef VOC_SAMPLES - if ( Mix_LoadVOC_RW(src, 0, - &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen) == NULL ) { -#endif - /* Load the WAV file into the chunk */ - if ( SDL_LoadWAV_RW(src, freesrc, - &wavespec, (Uint8 **)&chunk->abuf, &chunk->alen) == NULL ) { + /* Find out what kind of audio file this is */ + magic = SDL_ReadLE32(src); + /* Seek backwards for compatibility with older loaders */ + SDL_RWseek(src, -sizeof(Uint32), SEEK_CUR); + + switch (magic) { + case WAVE: + loaded = SDL_LoadWAV_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; + case FORM: + loaded = Mix_LoadAIFF_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; + default: + loaded = Mix_LoadVOC_RW(src, freesrc, &wavespec, + (Uint8 **)&chunk->abuf, &chunk->alen); + break; + } + if ( !loaded ) { free(chunk); return(NULL); } -#ifdef VOC_SAMPLES - } -#endif - #if 0 PrintFormat("Audio device", &mixer); PrintFormat("-- Wave file", &wavespec); diff --git a/wave.h b/wave.h deleted file mode 100644 index 4b9be2ad..00000000 --- a/wave.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - Taken with permission from SDL_wave.h, part of the SDL library, - available at: http://www.devolution.com/~slouken/SDL - and placed under the same license as this mixer library. -*/ - -/* WAVE files are little-endian */ - -/*******************************************/ -/* Define values for Microsoft WAVE format */ -/*******************************************/ -#define RIFF 0x46464952 /* "RIFF" */ -#define WAVE 0x45564157 /* "WAVE" */ -#define FACT 0x74636166 /* "fact" */ -#define LIST 0x5453494c /* "LIST" */ -#define FMT 0x20746D66 /* "fmt " */ -#define DATA 0x61746164 /* "data" */ -#define PCM_CODE 1 -#define ADPCM_CODE 2 -#define WAVE_MONO 1 -#define WAVE_STEREO 2 - -/* Normally, these three chunks come consecutively in a WAVE file */ -typedef struct WaveFMT { -/* Not saved in the chunk we read: - Uint32 FMTchunk; - Uint32 fmtlen; -*/ - Uint16 encoding; - Uint16 channels; /* 1 = mono, 2 = stereo */ - Uint32 frequency; /* One of 11025, 22050, or 44100 Hz */ - Uint32 byterate; /* Average bytes per second */ - Uint16 blockalign; /* Bytes per sample block */ - Uint16 bitspersample; /* One of 8, 12, 16, or 4 for ADPCM */ -} WaveFMT; - -/* The general chunk found in the WAVE file */ -typedef struct Chunk { - Uint32 magic; - Uint32 length; - Uint8 *data; /* Data includes magic and length */ -} Chunk; - -/*********************************************/ -/* Define values for AIFF (IFF audio) format */ -/*********************************************/ -#define FORM 0x4d524f46 /* "FORM" */ -#define AIFF 0x46464941 /* "AIFF" */ -#define SSND 0x444e5353 /* "SSND" */ -#define COMM 0x4d4d4f43 /* "COMM" */ - diff --git a/wavestream.c b/wavestream.c index d5cbc6e1..bdb100a1 100644 --- a/wavestream.c +++ b/wavestream.c @@ -32,9 +32,60 @@ #include "SDL_rwops.h" #include "SDL_endian.h" -#include "wave.h" #include "wavestream.h" +/* + Taken with permission from SDL_wave.h, part of the SDL library, + available at: http://www.libsdl.org/ + and placed under the same license as this mixer library. +*/ + +/* WAVE files are little-endian */ + +/*******************************************/ +/* Define values for Microsoft WAVE format */ +/*******************************************/ +#define RIFF 0x46464952 /* "RIFF" */ +#define WAVE 0x45564157 /* "WAVE" */ +#define FACT 0x74636166 /* "fact" */ +#define LIST 0x5453494c /* "LIST" */ +#define FMT 0x20746D66 /* "fmt " */ +#define DATA 0x61746164 /* "data" */ +#define PCM_CODE 1 +#define ADPCM_CODE 2 +#define WAVE_MONO 1 +#define WAVE_STEREO 2 + +/* Normally, these three chunks come consecutively in a WAVE file */ +typedef struct WaveFMT { +/* Not saved in the chunk we read: + Uint32 FMTchunk; + Uint32 fmtlen; +*/ + Uint16 encoding; + Uint16 channels; /* 1 = mono, 2 = stereo */ + Uint32 frequency; /* One of 11025, 22050, or 44100 Hz */ + Uint32 byterate; /* Average bytes per second */ + Uint16 blockalign; /* Bytes per sample block */ + Uint16 bitspersample; /* One of 8, 12, 16, or 4 for ADPCM */ +} WaveFMT; + +/* The general chunk found in the WAVE file */ +typedef struct Chunk { + Uint32 magic; + Uint32 length; + Uint8 *data; /* Data includes magic and length */ +} Chunk; + +/*********************************************/ +/* Define values for AIFF (IFF audio) format */ +/*********************************************/ +#define FORM 0x4d524f46 /* "FORM" */ +#define AIFF 0x46464941 /* "AIFF" */ +#define SSND 0x444e5353 /* "SSND" */ +#define COMM 0x4d4d4f43 /* "COMM" */ + + /* Currently we only support a single stream at a time */ static WAVStream *theWave = NULL;