aboutsummaryrefslogtreecommitdiffstats
path: root/distrib/sdl-1.2.15/src/audio/SDL_wave.c
diff options
context:
space:
mode:
Diffstat (limited to 'distrib/sdl-1.2.15/src/audio/SDL_wave.c')
-rw-r--r--distrib/sdl-1.2.15/src/audio/SDL_wave.c596
1 files changed, 596 insertions, 0 deletions
diff --git a/distrib/sdl-1.2.15/src/audio/SDL_wave.c b/distrib/sdl-1.2.15/src/audio/SDL_wave.c
new file mode 100644
index 0000000..b4ad6c7
--- /dev/null
+++ b/distrib/sdl-1.2.15/src/audio/SDL_wave.c
@@ -0,0 +1,596 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997-2012 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+ Sam Lantinga
+ slouken@libsdl.org
+*/
+#include "SDL_config.h"
+
+/* Microsoft WAVE file loading routines */
+
+#include "SDL_audio.h"
+#include "SDL_wave.h"
+
+
+static int ReadChunk(SDL_RWops *src, Chunk *chunk);
+
+struct MS_ADPCM_decodestate {
+ Uint8 hPredictor;
+ Uint16 iDelta;
+ Sint16 iSamp1;
+ Sint16 iSamp2;
+};
+static struct MS_ADPCM_decoder {
+ WaveFMT wavefmt;
+ Uint16 wSamplesPerBlock;
+ Uint16 wNumCoef;
+ Sint16 aCoeff[7][2];
+ /* * * */
+ struct MS_ADPCM_decodestate state[2];
+} MS_ADPCM_state;
+
+static int InitMS_ADPCM(WaveFMT *format)
+{
+ Uint8 *rogue_feel;
+ int i;
+
+ /* Set the rogue pointer to the MS_ADPCM specific data */
+ MS_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding);
+ MS_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels);
+ MS_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency);
+ MS_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate);
+ MS_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign);
+ MS_ADPCM_state.wavefmt.bitspersample =
+ SDL_SwapLE16(format->bitspersample);
+ rogue_feel = (Uint8 *)format+sizeof(*format);
+ if ( sizeof(*format) == 16 ) {
+ rogue_feel += sizeof(Uint16);
+ }
+ MS_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]);
+ rogue_feel += sizeof(Uint16);
+ MS_ADPCM_state.wNumCoef = ((rogue_feel[1]<<8)|rogue_feel[0]);
+ rogue_feel += sizeof(Uint16);
+ if ( MS_ADPCM_state.wNumCoef != 7 ) {
+ SDL_SetError("Unknown set of MS_ADPCM coefficients");
+ return(-1);
+ }
+ for ( i=0; i<MS_ADPCM_state.wNumCoef; ++i ) {
+ MS_ADPCM_state.aCoeff[i][0] = ((rogue_feel[1]<<8)|rogue_feel[0]);
+ rogue_feel += sizeof(Uint16);
+ MS_ADPCM_state.aCoeff[i][1] = ((rogue_feel[1]<<8)|rogue_feel[0]);
+ rogue_feel += sizeof(Uint16);
+ }
+ return(0);
+}
+
+static Sint32 MS_ADPCM_nibble(struct MS_ADPCM_decodestate *state,
+ Uint8 nybble, Sint16 *coeff)
+{
+ const Sint32 max_audioval = ((1<<(16-1))-1);
+ const Sint32 min_audioval = -(1<<(16-1));
+ const Sint32 adaptive[] = {
+ 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230
+ };
+ Sint32 new_sample, delta;
+
+ new_sample = ((state->iSamp1 * coeff[0]) +
+ (state->iSamp2 * coeff[1]))/256;
+ if ( nybble & 0x08 ) {
+ new_sample += state->iDelta * (nybble-0x10);
+ } else {
+ new_sample += state->iDelta * nybble;
+ }
+ if ( new_sample < min_audioval ) {
+ new_sample = min_audioval;
+ } else
+ if ( new_sample > max_audioval ) {
+ new_sample = max_audioval;
+ }
+ delta = ((Sint32)state->iDelta * adaptive[nybble])/256;
+ if ( delta < 16 ) {
+ delta = 16;
+ }
+ state->iDelta = (Uint16)delta;
+ state->iSamp2 = state->iSamp1;
+ state->iSamp1 = (Sint16)new_sample;
+ return(new_sample);
+}
+
+static int MS_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len)
+{
+ struct MS_ADPCM_decodestate *state[2];
+ Uint8 *freeable, *encoded, *decoded;
+ Sint32 encoded_len, samplesleft;
+ Sint8 nybble, stereo;
+ Sint16 *coeff[2];
+ Sint32 new_sample;
+
+ /* Allocate the proper sized output buffer */
+ encoded_len = *audio_len;
+ encoded = *audio_buf;
+ freeable = *audio_buf;
+ *audio_len = (encoded_len/MS_ADPCM_state.wavefmt.blockalign) *
+ MS_ADPCM_state.wSamplesPerBlock*
+ MS_ADPCM_state.wavefmt.channels*sizeof(Sint16);
+ *audio_buf = (Uint8 *)SDL_malloc(*audio_len);
+ if ( *audio_buf == NULL ) {
+ SDL_Error(SDL_ENOMEM);
+ return(-1);
+ }
+ decoded = *audio_buf;
+
+ /* Get ready... Go! */
+ stereo = (MS_ADPCM_state.wavefmt.channels == 2);
+ state[0] = &MS_ADPCM_state.state[0];
+ state[1] = &MS_ADPCM_state.state[stereo];
+ while ( encoded_len >= MS_ADPCM_state.wavefmt.blockalign ) {
+ /* Grab the initial information for this block */
+ state[0]->hPredictor = *encoded++;
+ if ( stereo ) {
+ state[1]->hPredictor = *encoded++;
+ }
+ state[0]->iDelta = ((encoded[1]<<8)|encoded[0]);
+ encoded += sizeof(Sint16);
+ if ( stereo ) {
+ state[1]->iDelta = ((encoded[1]<<8)|encoded[0]);
+ encoded += sizeof(Sint16);
+ }
+ state[0]->iSamp1 = ((encoded[1]<<8)|encoded[0]);
+ encoded += sizeof(Sint16);
+ if ( stereo ) {
+ state[1]->iSamp1 = ((encoded[1]<<8)|encoded[0]);
+ encoded += sizeof(Sint16);
+ }
+ state[0]->iSamp2 = ((encoded[1]<<8)|encoded[0]);
+ encoded += sizeof(Sint16);
+ if ( stereo ) {
+ state[1]->iSamp2 = ((encoded[1]<<8)|encoded[0]);
+ encoded += sizeof(Sint16);
+ }
+ coeff[0] = MS_ADPCM_state.aCoeff[state[0]->hPredictor];
+ coeff[1] = MS_ADPCM_state.aCoeff[state[1]->hPredictor];
+
+ /* Store the two initial samples we start with */
+ decoded[0] = state[0]->iSamp2&0xFF;
+ decoded[1] = state[0]->iSamp2>>8;
+ decoded += 2;
+ if ( stereo ) {
+ decoded[0] = state[1]->iSamp2&0xFF;
+ decoded[1] = state[1]->iSamp2>>8;
+ decoded += 2;
+ }
+ decoded[0] = state[0]->iSamp1&0xFF;
+ decoded[1] = state[0]->iSamp1>>8;
+ decoded += 2;
+ if ( stereo ) {
+ decoded[0] = state[1]->iSamp1&0xFF;
+ decoded[1] = state[1]->iSamp1>>8;
+ decoded += 2;
+ }
+
+ /* Decode and store the other samples in this block */
+ samplesleft = (MS_ADPCM_state.wSamplesPerBlock-2)*
+ MS_ADPCM_state.wavefmt.channels;
+ while ( samplesleft > 0 ) {
+ nybble = (*encoded)>>4;
+ new_sample = MS_ADPCM_nibble(state[0],nybble,coeff[0]);
+ decoded[0] = new_sample&0xFF;
+ new_sample >>= 8;
+ decoded[1] = new_sample&0xFF;
+ decoded += 2;
+
+ nybble = (*encoded)&0x0F;
+ new_sample = MS_ADPCM_nibble(state[1],nybble,coeff[1]);
+ decoded[0] = new_sample&0xFF;
+ new_sample >>= 8;
+ decoded[1] = new_sample&0xFF;
+ decoded += 2;
+
+ ++encoded;
+ samplesleft -= 2;
+ }
+ encoded_len -= MS_ADPCM_state.wavefmt.blockalign;
+ }
+ SDL_free(freeable);
+ return(0);
+}
+
+struct IMA_ADPCM_decodestate {
+ Sint32 sample;
+ Sint8 index;
+};
+static struct IMA_ADPCM_decoder {
+ WaveFMT wavefmt;
+ Uint16 wSamplesPerBlock;
+ /* * * */
+ struct IMA_ADPCM_decodestate state[2];
+} IMA_ADPCM_state;
+
+static int InitIMA_ADPCM(WaveFMT *format)
+{
+ Uint8 *rogue_feel;
+
+ /* Set the rogue pointer to the IMA_ADPCM specific data */
+ IMA_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding);
+ IMA_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels);
+ IMA_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency);
+ IMA_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate);
+ IMA_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign);
+ IMA_ADPCM_state.wavefmt.bitspersample =
+ SDL_SwapLE16(format->bitspersample);
+ rogue_feel = (Uint8 *)format+sizeof(*format);
+ if ( sizeof(*format) == 16 ) {
+ rogue_feel += sizeof(Uint16);
+ }
+ IMA_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]);
+ return(0);
+}
+
+static Sint32 IMA_ADPCM_nibble(struct IMA_ADPCM_decodestate *state,Uint8 nybble)
+{
+ const Sint32 max_audioval = ((1<<(16-1))-1);
+ const Sint32 min_audioval = -(1<<(16-1));
+ const int index_table[16] = {
+ -1, -1, -1, -1,
+ 2, 4, 6, 8,
+ -1, -1, -1, -1,
+ 2, 4, 6, 8
+ };
+ const Sint32 step_table[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31,
+ 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130,
+ 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282,
+ 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327,
+ 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630,
+ 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350,
+ 22385, 24623, 27086, 29794, 32767
+ };
+ Sint32 delta, step;
+
+ /* Compute difference and new sample value */
+ step = step_table[state->index];
+ delta = step >> 3;
+ if ( nybble & 0x04 ) delta += step;
+ if ( nybble & 0x02 ) delta += (step >> 1);
+ if ( nybble & 0x01 ) delta += (step >> 2);
+ if ( nybble & 0x08 ) delta = -delta;
+ state->sample += delta;
+
+ /* Update index value */
+ state->index += index_table[nybble];
+ if ( state->index > 88 ) {
+ state->index = 88;
+ } else
+ if ( state->index < 0 ) {
+ state->index = 0;
+ }
+
+ /* Clamp output sample */
+ if ( state->sample > max_audioval ) {
+ state->sample = max_audioval;
+ } else
+ if ( state->sample < min_audioval ) {
+ state->sample = min_audioval;
+ }
+ return(state->sample);
+}
+
+/* Fill the decode buffer with a channel block of data (8 samples) */
+static void Fill_IMA_ADPCM_block(Uint8 *decoded, Uint8 *encoded,
+ int channel, int numchannels, struct IMA_ADPCM_decodestate *state)
+{
+ int i;
+ Sint8 nybble;
+ Sint32 new_sample;
+
+ decoded += (channel * 2);
+ for ( i=0; i<4; ++i ) {
+ nybble = (*encoded)&0x0F;
+ new_sample = IMA_ADPCM_nibble(state, nybble);
+ decoded[0] = new_sample&0xFF;
+ new_sample >>= 8;
+ decoded[1] = new_sample&0xFF;
+ decoded += 2 * numchannels;
+
+ nybble = (*encoded)>>4;
+ new_sample = IMA_ADPCM_nibble(state, nybble);
+ decoded[0] = new_sample&0xFF;
+ new_sample >>= 8;
+ decoded[1] = new_sample&0xFF;
+ decoded += 2 * numchannels;
+
+ ++encoded;
+ }
+}
+
+static int IMA_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len)
+{
+ struct IMA_ADPCM_decodestate *state;
+ Uint8 *freeable, *encoded, *decoded;
+ Sint32 encoded_len, samplesleft;
+ unsigned int c, channels;
+
+ /* Check to make sure we have enough variables in the state array */
+ channels = IMA_ADPCM_state.wavefmt.channels;
+ if ( channels > SDL_arraysize(IMA_ADPCM_state.state) ) {
+ SDL_SetError("IMA ADPCM decoder can only handle %d channels",
+ SDL_arraysize(IMA_ADPCM_state.state));
+ return(-1);
+ }
+ state = IMA_ADPCM_state.state;
+
+ /* Allocate the proper sized output buffer */
+ encoded_len = *audio_len;
+ encoded = *audio_buf;
+ freeable = *audio_buf;
+ *audio_len = (encoded_len/IMA_ADPCM_state.wavefmt.blockalign) *
+ IMA_ADPCM_state.wSamplesPerBlock*
+ IMA_ADPCM_state.wavefmt.channels*sizeof(Sint16);
+ *audio_buf = (Uint8 *)SDL_malloc(*audio_len);
+ if ( *audio_buf == NULL ) {
+ SDL_Error(SDL_ENOMEM);
+ return(-1);
+ }
+ decoded = *audio_buf;
+
+ /* Get ready... Go! */
+ while ( encoded_len >= IMA_ADPCM_state.wavefmt.blockalign ) {
+ /* Grab the initial information for this block */
+ for ( c=0; c<channels; ++c ) {
+ /* Fill the state information for this block */
+ state[c].sample = ((encoded[1]<<8)|encoded[0]);
+ encoded += 2;
+ if ( state[c].sample & 0x8000 ) {
+ state[c].sample -= 0x10000;
+ }
+ state[c].index = *encoded++;
+ /* Reserved byte in buffer header, should be 0 */
+ if ( *encoded++ != 0 ) {
+ /* Uh oh, corrupt data? Buggy code? */;
+ }
+
+ /* Store the initial sample we start with */
+ decoded[0] = (Uint8)(state[c].sample&0xFF);
+ decoded[1] = (Uint8)(state[c].sample>>8);
+ decoded += 2;
+ }
+
+ /* Decode and store the other samples in this block */
+ samplesleft = (IMA_ADPCM_state.wSamplesPerBlock-1)*channels;
+ while ( samplesleft > 0 ) {
+ for ( c=0; c<channels; ++c ) {
+ Fill_IMA_ADPCM_block(decoded, encoded,
+ c, channels, &state[c]);
+ encoded += 4;
+ samplesleft -= 8;
+ }
+ decoded += (channels * 8 * 2);
+ }
+ encoded_len -= IMA_ADPCM_state.wavefmt.blockalign;
+ }
+ SDL_free(freeable);
+ return(0);
+}
+
+SDL_AudioSpec * SDL_LoadWAV_RW (SDL_RWops *src, int freesrc,
+ SDL_AudioSpec *spec, Uint8 **audio_buf, Uint32 *audio_len)
+{
+ int was_error;
+ Chunk chunk;
+ int lenread;
+ int MS_ADPCM_encoded, IMA_ADPCM_encoded;
+ int samplesize;
+
+ /* WAV magic header */
+ Uint32 RIFFchunk;
+ Uint32 wavelen = 0;
+ Uint32 WAVEmagic;
+ Uint32 headerDiff = 0;
+
+ /* FMT chunk */
+ WaveFMT *format = NULL;
+
+ /* Make sure we are passed a valid data source */
+ was_error = 0;
+ if ( src == NULL ) {
+ was_error = 1;
+ goto done;
+ }
+
+ /* Check the magic header */
+ RIFFchunk = SDL_ReadLE32(src);
+ wavelen = SDL_ReadLE32(src);
+ if ( wavelen == WAVE ) { /* The RIFFchunk has already been read */
+ WAVEmagic = wavelen;
+ wavelen = RIFFchunk;
+ RIFFchunk = RIFF;
+ } else {
+ WAVEmagic = SDL_ReadLE32(src);
+ }
+ if ( (RIFFchunk != RIFF) || (WAVEmagic != WAVE) ) {
+ SDL_SetError("Unrecognized file type (not WAVE)");
+ was_error = 1;
+ goto done;
+ }
+ headerDiff += sizeof(Uint32); /* for WAVE */
+
+ /* Read the audio data format chunk */
+ chunk.data = NULL;
+ do {
+ if ( chunk.data != NULL ) {
+ SDL_free(chunk.data);
+ chunk.data = NULL;
+ }
+ lenread = ReadChunk(src, &chunk);
+ if ( lenread < 0 ) {
+ was_error = 1;
+ goto done;
+ }
+ /* 2 Uint32's for chunk header+len, plus the lenread */
+ headerDiff += lenread + 2 * sizeof(Uint32);
+ } while ( (chunk.magic == FACT) || (chunk.magic == LIST) );
+
+ /* Decode the audio data format */
+ format = (WaveFMT *)chunk.data;
+ if ( chunk.magic != FMT ) {
+ SDL_SetError("Complex WAVE files not supported");
+ was_error = 1;
+ goto done;
+ }
+ MS_ADPCM_encoded = IMA_ADPCM_encoded = 0;
+ switch (SDL_SwapLE16(format->encoding)) {
+ case PCM_CODE:
+ /* We can understand this */
+ break;
+ case MS_ADPCM_CODE:
+ /* Try to understand this */
+ if ( InitMS_ADPCM(format) < 0 ) {
+ was_error = 1;
+ goto done;
+ }
+ MS_ADPCM_encoded = 1;
+ break;
+ case IMA_ADPCM_CODE:
+ /* Try to understand this */
+ if ( InitIMA_ADPCM(format) < 0 ) {
+ was_error = 1;
+ goto done;
+ }
+ IMA_ADPCM_encoded = 1;
+ break;
+ case MP3_CODE:
+ SDL_SetError("MPEG Layer 3 data not supported",
+ SDL_SwapLE16(format->encoding));
+ was_error = 1;
+ goto done;
+ default:
+ SDL_SetError("Unknown WAVE data format: 0x%.4x",
+ SDL_SwapLE16(format->encoding));
+ was_error = 1;
+ goto done;
+ }
+ SDL_memset(spec, 0, (sizeof *spec));
+ spec->freq = SDL_SwapLE32(format->frequency);
+ switch (SDL_SwapLE16(format->bitspersample)) {
+ case 4:
+ if ( MS_ADPCM_encoded || IMA_ADPCM_encoded ) {
+ spec->format = AUDIO_S16;
+ } else {
+ was_error = 1;
+ }
+ break;
+ case 8:
+ spec->format = AUDIO_U8;
+ break;
+ case 16:
+ spec->format = AUDIO_S16;
+ break;
+ default:
+ was_error = 1;
+ break;
+ }
+ if ( was_error ) {
+ SDL_SetError("Unknown %d-bit PCM data format",
+ SDL_SwapLE16(format->bitspersample));
+ goto done;
+ }
+ spec->channels = (Uint8)SDL_SwapLE16(format->channels);
+ spec->samples = 4096; /* Good default buffer size */
+
+ /* Read the audio data chunk */
+ *audio_buf = NULL;
+ do {
+ if ( *audio_buf != NULL ) {
+ SDL_free(*audio_buf);
+ *audio_buf = NULL;
+ }
+ lenread = ReadChunk(src, &chunk);
+ if ( lenread < 0 ) {
+ was_error = 1;
+ goto done;
+ }
+ *audio_len = lenread;
+ *audio_buf = chunk.data;
+ if(chunk.magic != DATA) headerDiff += lenread + 2 * sizeof(Uint32);
+ } while ( chunk.magic != DATA );
+ headerDiff += 2 * sizeof(Uint32); /* for the data chunk and len */
+
+ if ( MS_ADPCM_encoded ) {
+ if ( MS_ADPCM_decode(audio_buf, audio_len) < 0 ) {
+ was_error = 1;
+ goto done;
+ }
+ }
+ if ( IMA_ADPCM_encoded ) {
+ if ( IMA_ADPCM_decode(audio_buf, audio_len) < 0 ) {
+ was_error = 1;
+ goto done;
+ }
+ }
+
+ /* Don't return a buffer that isn't a multiple of samplesize */
+ samplesize = ((spec->format & 0xFF)/8)*spec->channels;
+ *audio_len &= ~(samplesize-1);
+
+done:
+ if ( format != NULL ) {
+ SDL_free(format);
+ }
+ if ( src ) {
+ if ( freesrc ) {
+ SDL_RWclose(src);
+ } else {
+ /* seek to the end of the file (given by the RIFF chunk) */
+ SDL_RWseek(src, wavelen - chunk.length - headerDiff, RW_SEEK_CUR);
+ }
+ }
+ if ( was_error ) {
+ spec = NULL;
+ }
+ return(spec);
+}
+
+/* Since the WAV memory is allocated in the shared library, it must also
+ be freed here. (Necessary under Win32, VC++)
+ */
+void SDL_FreeWAV(Uint8 *audio_buf)
+{
+ if ( audio_buf != NULL ) {
+ SDL_free(audio_buf);
+ }
+}
+
+static int ReadChunk(SDL_RWops *src, Chunk *chunk)
+{
+ chunk->magic = SDL_ReadLE32(src);
+ chunk->length = SDL_ReadLE32(src);
+ chunk->data = (Uint8 *)SDL_malloc(chunk->length);
+ if ( chunk->data == NULL ) {
+ SDL_Error(SDL_ENOMEM);
+ return(-1);
+ }
+ if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) {
+ SDL_Error(SDL_EFREAD);
+ SDL_free(chunk->data);
+ chunk->data = NULL;
+ return(-1);
+ }
+ return(chunk->length);
+}