summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/SDL2/src/audio/SDL_wave.c
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/SDL2/src/audio/SDL_wave.c')
-rw-r--r--3rdparty/SDL2/src/audio/SDL_wave.c624
1 files changed, 624 insertions, 0 deletions
diff --git a/3rdparty/SDL2/src/audio/SDL_wave.c b/3rdparty/SDL2/src/audio/SDL_wave.c
new file mode 100644
index 00000000000..99daac64ba7
--- /dev/null
+++ b/3rdparty/SDL2/src/audio/SDL_wave.c
@@ -0,0 +1,624 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+#include "../SDL_internal.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) {
+ /* const Uint16 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); */
+ 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;
+ Uint8 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) {
+ return SDL_OutOfMemory();
+ }
+ 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) {
+ /* const Uint16 extra_info = ((rogue_feel[1] << 8) | rogue_feel[0]); */
+ 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 */
+ if (state->index > 88) {
+ state->index = 88;
+ } else if (state->index < 0) {
+ state->index = 0;
+ }
+ /* explicit cast to avoid gcc warning about using 'char' as array index */
+ step = step_table[(int)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];
+
+ /* 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 %u channels",
+ (unsigned int)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) {
+ return SDL_OutOfMemory();
+ }
+ 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 IEEE_float_encoded, 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;
+
+ SDL_zero(chunk);
+
+ /* 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 {
+ 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) || (chunk.magic == BEXT) || (chunk.magic == JUNK));
+
+ /* 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;
+ }
+ IEEE_float_encoded = MS_ADPCM_encoded = IMA_ADPCM_encoded = 0;
+ switch (SDL_SwapLE16(format->encoding)) {
+ case PCM_CODE:
+ /* We can understand this */
+ break;
+ case IEEE_FLOAT_CODE:
+ IEEE_float_encoded = 1;
+ /* 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");
+ 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);
+
+ if (IEEE_float_encoded) {
+ if ((SDL_SwapLE16(format->bitspersample)) != 32) {
+ was_error = 1;
+ } else {
+ spec->format = AUDIO_F32;
+ }
+ } else {
+ 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;
+ case 32:
+ spec->format = AUDIO_S32;
+ 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 {
+ 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 = ((SDL_AUDIO_BITSIZE(spec->format)) / 8) * spec->channels;
+ *audio_len &= ~(samplesize - 1);
+
+ done:
+ 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)
+{
+ 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) {
+ return SDL_OutOfMemory();
+ }
+ if (SDL_RWread(src, chunk->data, chunk->length, 1) != 1) {
+ SDL_free(chunk->data);
+ chunk->data = NULL;
+ return SDL_Error(SDL_EFREAD);
+ }
+ return (chunk->length);
+}
+
+/* vi: set ts=4 sw=4 expandtab: */