diff --git a/.gitignore b/.gitignore index a18b8db7..7c5ac7df 100644 --- a/.gitignore +++ b/.gitignore @@ -5,8 +5,6 @@ *.frag.xxd *.ttf.xxd -Makefile - mkxp xxd+ diff --git a/SDL2_sound/SDL_sound.c b/SDL2_sound/SDL_sound.c new file mode 100644 index 00000000..e08f8465 --- /dev/null +++ b/SDL2_sound/SDL_sound.c @@ -0,0 +1,795 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/** + * This file implements the core API, which is relatively simple. + * The real meat of SDL_sound is in the decoders. + * + * Documentation is in SDL_sound.h ... It's verbose, honest. :) + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +/* The various decoder drivers... */ + +/* All these externs may be missing; we check SOUND_SUPPORTS_xxx before use. */ +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MODPLUG; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_AIFF; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_AU; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_VOC; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_RAW; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_SHN; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_QuickTime; +extern const Sound_DecoderFunctions __Sound_DecoderFunctions_CoreAudio; + +typedef struct +{ + int available; + const Sound_DecoderFunctions *funcs; +} decoder_element; + +static decoder_element decoders[] = +{ +#if SOUND_SUPPORTS_MODPLUG + { 0, &__Sound_DecoderFunctions_MODPLUG }, +#endif +#if SOUND_SUPPORTS_MP3 + { 0, &__Sound_DecoderFunctions_MP3 }, +#endif +#if SOUND_SUPPORTS_WAV + { 0, &__Sound_DecoderFunctions_WAV }, +#endif +#if SOUND_SUPPORTS_AIFF + { 0, &__Sound_DecoderFunctions_AIFF }, +#endif +#if SOUND_SUPPORTS_AU + { 0, &__Sound_DecoderFunctions_AU }, +#endif +#if SOUND_SUPPORTS_VORBIS + { 0, &__Sound_DecoderFunctions_VORBIS }, +#endif +#if SOUND_SUPPORTS_VOC + { 0, &__Sound_DecoderFunctions_VOC }, +#endif +#if SOUND_SUPPORTS_RAW + { 0, &__Sound_DecoderFunctions_RAW }, +#endif +#if SOUND_SUPPORTS_SHN + { 0, &__Sound_DecoderFunctions_SHN }, +#endif +#if SOUND_SUPPORTS_FLAC + { 0, &__Sound_DecoderFunctions_FLAC }, +#endif +#if SOUND_SUPPORTS_COREAUDIO + { 0, &__Sound_DecoderFunctions_CoreAudio }, +#endif + + { 0, NULL } +}; + + + +/* General SDL_sound state ... */ + +typedef struct __SOUND_ERRMSGTYPE__ +{ + Uint32 tid; + int error_available; + char error_string[128]; + struct __SOUND_ERRMSGTYPE__ *next; +} ErrMsg; + +static ErrMsg *error_msgs = NULL; +static SDL_mutex *errorlist_mutex = NULL; + +static Sound_Sample *sample_list = NULL; /* this is a linked list. */ +static SDL_mutex *samplelist_mutex = NULL; + +static const Sound_DecoderInfo **available_decoders = NULL; +static int initialized = 0; + + +/* functions ... */ + +void Sound_GetLinkedVersion(Sound_Version *ver) +{ + if (ver != NULL) + { + ver->major = SOUND_VER_MAJOR; + ver->minor = SOUND_VER_MINOR; + ver->patch = SOUND_VER_PATCH; + } /* if */ +} /* Sound_GetLinkedVersion */ + + +int Sound_Init(void) +{ + size_t i; + size_t pos = 0; + size_t total = sizeof (decoders) / sizeof (decoders[0]); + BAIL_IF_MACRO(initialized, ERR_IS_INITIALIZED, 0); + + sample_list = NULL; + error_msgs = NULL; + + available_decoders = (const Sound_DecoderInfo **) + SDL_calloc(total, sizeof (Sound_DecoderInfo *)); + BAIL_IF_MACRO(available_decoders == NULL, ERR_OUT_OF_MEMORY, 0); + + SDL_InitSubSystem(SDL_INIT_AUDIO); + + errorlist_mutex = SDL_CreateMutex(); + samplelist_mutex = SDL_CreateMutex(); + + for (i = 0; decoders[i].funcs != NULL; i++) + { + decoders[i].available = decoders[i].funcs->init(); + if (decoders[i].available) + { + available_decoders[pos] = &(decoders[i].funcs->info); + pos++; + } /* if */ + } /* for */ + + initialized = 1; + return 1; +} /* Sound_Init */ + + +int Sound_Quit(void) +{ + ErrMsg *err; + ErrMsg *nexterr = NULL; + size_t i; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + + while (((volatile Sound_Sample *) sample_list) != NULL) + Sound_FreeSample(sample_list); + + initialized = 0; + + SDL_DestroyMutex(samplelist_mutex); + samplelist_mutex = NULL; + sample_list = NULL; + + for (i = 0; decoders[i].funcs != NULL; i++) + { + if (decoders[i].available) + { + decoders[i].funcs->quit(); + decoders[i].available = 0; + } /* if */ + } /* for */ + + if (available_decoders != NULL) + SDL_free((void *) available_decoders); + available_decoders = NULL; + + /* clean up error state for each thread... */ + SDL_LockMutex(errorlist_mutex); + for (err = error_msgs; err != NULL; err = nexterr) + { + nexterr = err->next; + SDL_free(err); + } /* for */ + error_msgs = NULL; + SDL_UnlockMutex(errorlist_mutex); + SDL_DestroyMutex(errorlist_mutex); + errorlist_mutex = NULL; + + return 1; +} /* Sound_Quit */ + + +const Sound_DecoderInfo **Sound_AvailableDecoders(void) +{ + return available_decoders; /* READ. ONLY. */ +} /* Sound_AvailableDecoders */ + + +static ErrMsg *findErrorForCurrentThread(void) +{ + ErrMsg *i; + Uint32 tid; + + if (error_msgs != NULL) + { + tid = SDL_ThreadID(); + + SDL_LockMutex(errorlist_mutex); + for (i = error_msgs; i != NULL; i = i->next) + { + if (i->tid == tid) + { + SDL_UnlockMutex(errorlist_mutex); + return i; + } /* if */ + } /* for */ + SDL_UnlockMutex(errorlist_mutex); + } /* if */ + + return NULL; /* no error available. */ +} /* findErrorForCurrentThread */ + + +const char *Sound_GetError(void) +{ + const char *retval = NULL; + ErrMsg *err; + + if (!initialized) + return ERR_NOT_INITIALIZED; + + err = findErrorForCurrentThread(); + if ((err != NULL) && (err->error_available)) + { + retval = err->error_string; + err->error_available = 0; + } /* if */ + + return retval; +} /* Sound_GetError */ + + +void Sound_ClearError(void) +{ + ErrMsg *err; + + if (!initialized) + return; + + err = findErrorForCurrentThread(); + if (err != NULL) + err->error_available = 0; +} /* Sound_ClearError */ + + +/* + * This is declared in the internal header. + */ +void __Sound_SetError(const char *str) +{ + ErrMsg *err; + + if (str == NULL) + return; + + SNDDBG(("__Sound_SetError(\"%s\");%s\n", str, + (initialized) ? "" : " [NOT INITIALIZED!]")); + + if (!initialized) + return; + + err = findErrorForCurrentThread(); + if (err == NULL) + { + err = (ErrMsg *) SDL_calloc(1, sizeof (ErrMsg)); + if (err == NULL) + return; /* uhh...? */ + + err->tid = SDL_ThreadID(); + + SDL_LockMutex(errorlist_mutex); + err->next = error_msgs; + error_msgs = err; + SDL_UnlockMutex(errorlist_mutex); + } /* if */ + + err->error_available = 1; + SDL_strlcpy(err->error_string, str, sizeof (err->error_string)); +} /* __Sound_SetError */ + + +Uint32 __Sound_convertMsToBytePos(Sound_AudioInfo *info, Uint32 ms) +{ + /* "frames" == "sample frames" */ + float frames_per_ms = ((float) info->rate) / 1000.0f; + Uint32 frame_offset = (Uint32) (frames_per_ms * ((float) ms)); + Uint32 frame_size = (Uint32) ((info->format & 0xFF) / 8) * info->channels; + return frame_offset * frame_size; +} /* __Sound_convertMsToBytePos */ + + +/* + * Allocate a Sound_Sample, and fill in most of its fields. Those that need + * to be filled in later, by a decoder, will be initialized to zero. + */ +static Sound_Sample *alloc_sample(SDL_RWops *rw, Sound_AudioInfo *desired, + Uint32 bufferSize) +{ + /* + * !!! FIXME: We're going to need to pool samples, since the mixer + * !!! FIXME: might be allocating tons of these on a regular basis. + */ + Sound_Sample *retval = SDL_calloc(1, sizeof (Sound_Sample)); + Sound_SampleInternal *internal = SDL_calloc(1, sizeof (Sound_SampleInternal)); + if ((retval == NULL) || (internal == NULL)) + { + __Sound_SetError(ERR_OUT_OF_MEMORY); + if (retval) + SDL_free(retval); + if (internal) + SDL_free(internal); + + return NULL; + } /* if */ + + SDL_assert(bufferSize > 0); + retval->buffer = SDL_calloc(1, bufferSize); /* pure ugly. */ + if (!retval->buffer) + { + __Sound_SetError(ERR_OUT_OF_MEMORY); + SDL_free(internal); + SDL_free(retval); + return NULL; + } /* if */ + retval->buffer_size = bufferSize; + + if (desired != NULL) + SDL_memcpy(&retval->desired, desired, sizeof (Sound_AudioInfo)); + + internal->rw = rw; + retval->opaque = internal; + return retval; +} /* alloc_sample */ + + +#if (defined DEBUG_CHATTER) +static SDL_INLINE const char *fmt_to_str(Uint16 fmt) +{ + switch(fmt) + { + case AUDIO_U8: + return "U8"; + case AUDIO_S8: + return "S8"; + case AUDIO_U16LSB: + return "U16LSB"; + case AUDIO_S16LSB: + return "S16LSB"; + case AUDIO_S32LSB: + return "S32LSB"; + case AUDIO_F32LSB: + return "F32LSB"; + case AUDIO_U16MSB: + return "U16MSB"; + case AUDIO_S16MSB: + return "S16MSB"; + case AUDIO_S32MSB: + return "S32MSB"; + case AUDIO_F32MSB: + return "F32MSB"; + } /* switch */ + + return "Unknown"; +} /* fmt_to_str */ +#endif + + +/* + * The bulk of the Sound_NewSample() work is done here... + * Ask the specified decoder to handle the data in (rw), and if + * so, construct the Sound_Sample. Otherwise, try to wind (rw)'s stream + * back to where it was, and return false. + */ +static int init_sample(const Sound_DecoderFunctions *funcs, + Sound_Sample *sample, const char *ext, + Sound_AudioInfo *_desired) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + Sound_AudioInfo desired; + int pos = SDL_RWtell(internal->rw); + + /* fill in the funcs for this decoder... */ + sample->decoder = &funcs->info; + internal->funcs = funcs; + if (!funcs->open(sample, ext)) + { + SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */ + return 0; + } /* if */ + + /* success; we've got a decoder! */ + + /* Now we need to set up the conversion buffer... */ + + if (_desired == NULL) + SDL_memcpy(&desired, &sample->actual, sizeof (Sound_AudioInfo)); + else + { + desired.format = _desired->format ? _desired->format : sample->actual.format; + desired.channels = _desired->channels ? _desired->channels : sample->actual.channels; + desired.rate = _desired->rate ? _desired->rate : sample->actual.rate; + } /* else */ + + if (SDL_BuildAudioCVT(&internal->sdlcvt, + sample->actual.format, + sample->actual.channels, + sample->actual.rate, + desired.format, + desired.channels, + desired.rate) == -1) + { + __Sound_SetError(SDL_GetError()); + funcs->close(sample); + SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */ + return 0; + } /* if */ + + if (internal->sdlcvt.len_mult > 1) + { + void *rc = SDL_realloc(sample->buffer, sample->buffer_size * internal->sdlcvt.len_mult); + if (rc == NULL) + { + funcs->close(sample); + SDL_RWseek(internal->rw, pos, SEEK_SET); /* set for next try... */ + return 0; + } /* if */ + + sample->buffer = rc; + } /* if */ + + /* these pointers are all one and the same. */ + SDL_memcpy(&sample->desired, &desired, sizeof (Sound_AudioInfo)); + internal->sdlcvt.buf = internal->buffer = sample->buffer; + internal->buffer_size = sample->buffer_size / internal->sdlcvt.len_mult; + internal->sdlcvt.len = internal->buffer_size; + + /* Prepend our new Sound_Sample to the sample_list... */ + SDL_LockMutex(samplelist_mutex); + internal->next = sample_list; + if (sample_list != NULL) + ((Sound_SampleInternal *) sample_list->opaque)->prev = sample; + sample_list = sample; + SDL_UnlockMutex(samplelist_mutex); + + SNDDBG(("New sample DESIRED format: %s format, %d rate, %d channels.\n", + fmt_to_str(sample->desired.format), + sample->desired.rate, + sample->desired.channels)); + + SNDDBG(("New sample ACTUAL format: %s format, %d rate, %d channels.\n", + fmt_to_str(sample->actual.format), + sample->actual.rate, + sample->actual.channels)); + + SNDDBG(("On-the-fly conversion: %s.\n", + internal->sdlcvt.needed ? "ENABLED" : "DISABLED")); + + return 1; +} /* init_sample */ + + +Sound_Sample *Sound_NewSample(SDL_RWops *rw, const char *ext, + Sound_AudioInfo *desired, Uint32 bSize) +{ + Sound_Sample *retval; + decoder_element *decoder; + + /* sanity checks. */ + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL); + BAIL_IF_MACRO(rw == NULL, ERR_INVALID_ARGUMENT, NULL); + + retval = alloc_sample(rw, desired, bSize); + if (!retval) + return NULL; /* alloc_sample() sets error message... */ + + if (ext != NULL) + { + for (decoder = &decoders[0]; decoder->funcs != NULL; decoder++) + { + if (decoder->available) + { + const char **decoderExt = decoder->funcs->info.extensions; + while (*decoderExt) + { + if (SDL_strcasecmp(*decoderExt, ext) == 0) + { + if (init_sample(decoder->funcs, retval, ext, desired)) + return retval; + break; /* done with this decoder either way. */ + } /* if */ + decoderExt++; + } /* while */ + } /* if */ + } /* for */ + } /* if */ + + /* no direct extension match? Try everything we've got... */ + for (decoder = &decoders[0]; decoder->funcs != NULL; decoder++) + { + if (decoder->available) + { + int should_try = 1; + const char **decoderExt = decoder->funcs->info.extensions; + + /* skip if we would have tried decoder above... */ + while (*decoderExt) + { + if (SDL_strcasecmp(*decoderExt, ext) == 0) + { + should_try = 0; + break; + } /* if */ + decoderExt++; + } /* while */ + + if (should_try) + { + if (init_sample(decoder->funcs, retval, ext, desired)) + return retval; + } /* if */ + } /* if */ + } /* for */ + + /* nothing could handle the sound data... */ + SDL_free(retval->opaque); + if (retval->buffer != NULL) + SDL_free(retval->buffer); + SDL_free(retval); + SDL_RWclose(rw); + __Sound_SetError(ERR_UNSUPPORTED_FORMAT); + return NULL; +} /* Sound_NewSample */ + + +Sound_Sample *Sound_NewSampleFromFile(const char *filename, + Sound_AudioInfo *desired, + Uint32 bufferSize) +{ + const char *ext; + SDL_RWops *rw; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL); + BAIL_IF_MACRO(filename == NULL, ERR_INVALID_ARGUMENT, NULL); + + ext = SDL_strrchr(filename, '.'); + rw = SDL_RWFromFile(filename, "rb"); + BAIL_IF_MACRO(rw == NULL, SDL_GetError(), NULL); + + if (ext != NULL) + ext++; + + return Sound_NewSample(rw, ext, desired, bufferSize); +} /* Sound_NewSampleFromFile */ + + +Sound_Sample *Sound_NewSampleFromMem(const Uint8 *data, + Uint32 size, + const char *ext, + Sound_AudioInfo *desired, + Uint32 bufferSize) +{ + SDL_RWops *rw; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, NULL); + BAIL_IF_MACRO(data == NULL, ERR_INVALID_ARGUMENT, NULL); + BAIL_IF_MACRO(size == 0, ERR_INVALID_ARGUMENT, NULL); + + rw = SDL_RWFromConstMem(data, size); + BAIL_IF_MACRO(rw == NULL, SDL_GetError(), NULL); + + return Sound_NewSample(rw, ext, desired, bufferSize); +} /* Sound_NewSampleFromMem */ + + +void Sound_FreeSample(Sound_Sample *sample) +{ + Sound_SampleInternal *internal; + + if (!initialized) + { + __Sound_SetError(ERR_NOT_INITIALIZED); + return; + } /* if */ + + if (sample == NULL) + { + __Sound_SetError(ERR_INVALID_ARGUMENT); + return; + } /* if */ + + internal = (Sound_SampleInternal *) sample->opaque; + + SDL_LockMutex(samplelist_mutex); + + /* update the sample_list... */ + if (internal->prev != NULL) + { + Sound_SampleInternal *prevInternal; + prevInternal = (Sound_SampleInternal *) internal->prev->opaque; + prevInternal->next = internal->next; + } /* if */ + else + { + SDL_assert(sample_list == sample); + sample_list = internal->next; + } /* else */ + + if (internal->next != NULL) + { + Sound_SampleInternal *nextInternal; + nextInternal = (Sound_SampleInternal *) internal->next->opaque; + nextInternal->prev = internal->prev; + } /* if */ + + SDL_UnlockMutex(samplelist_mutex); + + /* nuke it... */ + internal->funcs->close(sample); + + if (internal->rw != NULL) /* this condition is a "just in case" thing. */ + SDL_RWclose(internal->rw); + + if ((internal->buffer != NULL) && (internal->buffer != sample->buffer)) + SDL_free(internal->buffer); + + SDL_free(internal); + + if (sample->buffer != NULL) + SDL_free(sample->buffer); + + SDL_free(sample); +} /* Sound_FreeSample */ + + +int Sound_SetBufferSize(Sound_Sample *sample, Uint32 newSize) +{ + void *newBuf = NULL; + Sound_SampleInternal *internal = NULL; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0); + internal = ((Sound_SampleInternal *) sample->opaque); + newBuf = SDL_realloc(sample->buffer, newSize * internal->sdlcvt.len_mult); + BAIL_IF_MACRO(newBuf == NULL, ERR_OUT_OF_MEMORY, 0); + + internal->sdlcvt.buf = internal->buffer = sample->buffer = newBuf; + sample->buffer_size = newSize; + internal->buffer_size = newSize / internal->sdlcvt.len_mult; + internal->sdlcvt.len = internal->buffer_size; + + return 1; +} /* Sound_SetBufferSize */ + + +Uint32 Sound_Decode(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = NULL; + Uint32 retval = 0; + + /* a boatload of sanity checks... */ + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + BAIL_IF_MACRO(sample == NULL, ERR_INVALID_ARGUMENT, 0); + BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREV_ERROR, 0); + BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_PREV_EOF, 0); + + internal = (Sound_SampleInternal *) sample->opaque; + + SDL_assert(sample->buffer != NULL); + SDL_assert(sample->buffer_size > 0); + SDL_assert(internal->buffer != NULL); + SDL_assert(internal->buffer_size > 0); + + /* reset EAGAIN. Decoder can flip it back on if it needs to. */ + sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN; + retval = internal->funcs->read(sample); + + if (retval > 0 && internal->sdlcvt.needed) + { + internal->sdlcvt.len = retval; + SDL_ConvertAudio(&internal->sdlcvt); + retval = internal->sdlcvt.len_cvt; + } /* if */ + + return retval; +} /* Sound_Decode */ + + +Uint32 Sound_DecodeAll(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = NULL; + void *buf = NULL; + Uint32 newBufSize = 0; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_EOF, ERR_PREV_EOF, 0); + BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_PREV_ERROR, 0); + + internal = (Sound_SampleInternal *) sample->opaque; + + while ( ((sample->flags & SOUND_SAMPLEFLAG_EOF) == 0) && + ((sample->flags & SOUND_SAMPLEFLAG_ERROR) == 0) ) + { + Uint32 br = Sound_Decode(sample); + void *ptr = SDL_realloc(buf, newBufSize + br); + if (ptr == NULL) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + __Sound_SetError(ERR_OUT_OF_MEMORY); + } /* if */ + else + { + buf = ptr; + SDL_memcpy( ((char *) buf) + newBufSize, sample->buffer, br ); + newBufSize += br; + } /* else */ + } /* while */ + + if (buf == NULL) /* ...in case first call to SDL_realloc() fails... */ + return sample->buffer_size; + + if (internal->buffer != sample->buffer) + SDL_free(internal->buffer); + + SDL_free(sample->buffer); + + internal->sdlcvt.buf = internal->buffer = sample->buffer = buf; + sample->buffer_size = newBufSize; + internal->buffer_size = newBufSize / internal->sdlcvt.len_mult; + internal->sdlcvt.len = internal->buffer_size; + + return newBufSize; +} /* Sound_DecodeAll */ + + +int Sound_Rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal; + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + + internal = (Sound_SampleInternal *) sample->opaque; + if (!internal->funcs->rewind(sample)) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return 0; + } /* if */ + + sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN; + sample->flags &= ~SOUND_SAMPLEFLAG_ERROR; + sample->flags &= ~SOUND_SAMPLEFLAG_EOF; + + return 1; +} /* Sound_Rewind */ + + +int Sound_Seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal; + + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0); + if (!(sample->flags & SOUND_SAMPLEFLAG_CANSEEK)) + BAIL_MACRO(ERR_CANNOT_SEEK, 0); + + internal = (Sound_SampleInternal *) sample->opaque; + BAIL_IF_MACRO(!internal->funcs->seek(sample, ms), NULL, 0); + + sample->flags &= ~SOUND_SAMPLEFLAG_EAGAIN; + sample->flags &= ~SOUND_SAMPLEFLAG_ERROR; + sample->flags &= ~SOUND_SAMPLEFLAG_EOF; + + return 1; +} /* Sound_Rewind */ + + +Sint32 Sound_GetDuration(Sound_Sample *sample) +{ + Sound_SampleInternal *internal; + BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, -1); + internal = (Sound_SampleInternal *) sample->opaque; + return internal->total_time; +} /* Sound_GetDuration */ + +/* end of SDL_sound.c ... */ + diff --git a/SDL2_sound/SDL_sound.h b/SDL2_sound/SDL_sound.h new file mode 100644 index 00000000..ee57eaf6 --- /dev/null +++ b/SDL2_sound/SDL_sound.h @@ -0,0 +1,725 @@ +/** \file SDL_sound.h */ + +/* + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + */ + +/** + * \mainpage SDL_sound + * + * The latest version of SDL_sound can be found at: + * https://icculus.org/SDL_sound/ + * + * The basic gist of SDL_sound is that you use an SDL_RWops to get sound data + * into this library, and SDL_sound will take that data, in one of several + * popular formats, and decode it into raw waveform data in the format of + * your choice. This gives you a nice abstraction for getting sound into your + * game or application; just feed it to SDL_sound, and it will handle + * decoding and converting, so you can just pass it to your SDL audio + * callback (or whatever). Since it gets data from an SDL_RWops, you can get + * the initial sound data from any number of sources: file, memory buffer, + * network connection, etc. + * + * As the name implies, this library depends on SDL: Simple Directmedia Layer, + * which is a powerful, free, and cross-platform multimedia library. It can + * be found at https://www.libsdl.org/ + * + * Support is in place or planned for the following sound formats: + * - .WAV (Microsoft WAVfile RIFF data, internal.) + * - .VOC (Creative Labs' Voice format, internal.) + * - .MP3 (MPEG-1 Layer 3 support, via libmpg123.) + * - .MID (MIDI music converted to Waveform data, internal.) + * - .MOD (MOD files, via internal copy of libModPlug.) + * - .OGG (Ogg files, via internal copy of stb_vorbis.) + * - .SHN (Shorten files, internal.) + * - .RAW (Raw sound data in any format, internal.) + * - .AU (Sun's Audio format, internal.) + * - .AIFF (Audio Interchange format, internal.) + * - .FLAC (Lossless audio compression, via libFLAC.) + * + * (...and more to come...) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * \author Ryan C. Gordon (icculus@icculus.org) + * \author many others, please see CREDITS in the source's root directory. + */ + +#ifndef _INCLUDE_SDL_SOUND_H_ +#define _INCLUDE_SDL_SOUND_H_ + +#include "SDL.h" + +#if SDL_MAJOR_VERSION < 2 +#error SDL2_sound requires SDL 2.0.0 or later. +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS + +#ifdef SDL_SOUND_DLL_EXPORTS +# define SNDDECLSPEC __declspec(dllexport) +#elif (__GNUC__ >= 3) +# define SNDDECLSPEC __attribute__((visibility("default"))) +#else +# define SNDDECLSPEC +#endif + +#define SOUND_VER_MAJOR 1 +#define SOUND_VER_MINOR 9 +#define SOUND_VER_PATCH 0 +#endif + + +/** + * \enum Sound_SampleFlags + * \brief Flags that are used in a Sound_Sample to show various states. + * + * To use: + * \code + * if (sample->flags & SOUND_SAMPLEFLAG_ERROR) { dosomething(); } + * \endcode + * + * \sa Sound_SampleNew + * \sa Sound_SampleNewFromFile + * \sa Sound_SampleDecode + * \sa Sound_SampleDecodeAll + * \sa Sound_SampleSeek + */ +typedef enum +{ + SOUND_SAMPLEFLAG_NONE = 0, /**< No special attributes. */ + + /* these are set at sample creation time... */ + SOUND_SAMPLEFLAG_CANSEEK = 1, /**< Sample can seek to arbitrary points. */ + + /* these are set during decoding... */ + SOUND_SAMPLEFLAG_EOF = 1 << 29, /**< End of input stream. */ + SOUND_SAMPLEFLAG_ERROR = 1 << 30, /**< Unrecoverable error. */ + SOUND_SAMPLEFLAG_EAGAIN = 1 << 31 /**< Function would block, or temp error. */ +} Sound_SampleFlags; + + +/** + * \struct Sound_AudioInfo + * \brief Information about an existing sample's format. + * + * These are the basics of a decoded sample's data structure: data format + * (see AUDIO_U8 and friends in SDL_audio.h), number of channels, and sample + * rate. If you need more explanation than that, you should stop developing + * sound code right now. + * + * \sa Sound_SampleNew + * \sa Sound_SampleNewFromFile + */ +typedef struct +{ + Uint16 format; /**< Equivalent of SDL_AudioSpec.format. */ + Uint8 channels; /**< Number of sound channels. 1 == mono, 2 == stereo. */ + Uint32 rate; /**< Sample rate; frequency of sample points per second. */ +} Sound_AudioInfo; + + +/** + * \struct Sound_DecoderInfo + * \brief Information about available soudn decoders. + * + * Each decoder sets up one of these structs, which can be retrieved via + * the Sound_AvailableDecoders() function. EVERY FIELD IN THIS IS READ-ONLY. + * + * The extensions field is a NULL-terminated list of ASCIZ strings. You + * should read it like this: + * + * \code + * const char **ext; + * for (ext = info->extensions; *ext != NULL; ext++) { + * printf(" File extension \"%s\"\n", *ext); + * } + * \endcode + * + * \sa Sound_AvailableDecoders + */ +typedef struct +{ + const char **extensions; /**< File extensions, list ends with NULL. */ + const char *description; /**< Human readable description of decoder. */ + const char *author; /**< "Name Of Author \" */ + const char *url; /**< URL specific to this decoder. */ +} Sound_DecoderInfo; + + + +/** + * \struct Sound_Sample + * \brief Represents sound data in the process of being decoded. + * + * The Sound_Sample structure is the heart of SDL_sound. This holds + * information about a source of sound data as it is being decoded. + * EVERY FIELD IN THIS IS READ-ONLY. Please use the API functions to + * change them. + */ +typedef struct +{ + void *opaque; /**< Internal use only. Don't touch. */ + const Sound_DecoderInfo *decoder; /**< Decoder used for this sample. */ + Sound_AudioInfo desired; /**< Desired audio format for conversion. */ + Sound_AudioInfo actual; /**< Actual audio format of sample. */ + void *buffer; /**< Decoded sound data lands in here. */ + Uint32 buffer_size; /**< Current size of (buffer), in bytes (Uint8). */ + Sound_SampleFlags flags; /**< Flags relating to this sample. */ +} Sound_Sample; + + +/** + * \struct Sound_Version + * \brief Information the version of SDL_sound in use. + * + * Represents the library's version as three levels: major revision + * (increments with massive changes, additions, and enhancements), + * minor revision (increments with backwards-compatible changes to the + * major revision), and patchlevel (increments with fixes to the minor + * revision). + * + * \sa SOUND_VERSION + * \sa Sound_GetLinkedVersion + */ +typedef struct +{ + int major; /**< major revision */ + int minor; /**< minor revision */ + int patch; /**< patchlevel */ +} Sound_Version; + + +/* functions and macros... */ + +/** + * \def SOUND_VERSION(x) + * \brief Macro to determine SDL_sound version program was compiled against. + * + * This macro fills in a Sound_Version structure with the version of the + * library you compiled against. This is determined by what header the + * compiler uses. Note that if you dynamically linked the library, you might + * have a slightly newer or older version at runtime. That version can be + * determined with Sound_GetLinkedVersion(), which, unlike SOUND_VERSION, + * is not a macro. + * + * \param x A pointer to a Sound_Version struct to initialize. + * + * \sa Sound_Version + * \sa Sound_GetLinkedVersion + */ +#define SOUND_VERSION(x) \ +{ \ + (x)->major = SOUND_VER_MAJOR; \ + (x)->minor = SOUND_VER_MINOR; \ + (x)->patch = SOUND_VER_PATCH; \ +} + + +/** + * \fn void Sound_GetLinkedVersion(Sound_Version *ver) + * \brief Get the version of SDL_sound that is linked against your program. + * + * If you are using a shared library (DLL) version of SDL_sound, then it is + * possible that it will be different than the version you compiled against. + * + * This is a real function; the macro SOUND_VERSION tells you what version + * of SDL_sound you compiled against: + * + * \code + * Sound_Version compiled; + * Sound_Version linked; + * + * SOUND_VERSION(&compiled); + * Sound_GetLinkedVersion(&linked); + * printf("We compiled against SDL_sound version %d.%d.%d ...\n", + * compiled.major, compiled.minor, compiled.patch); + * printf("But we linked against SDL_sound version %d.%d.%d.\n", + * linked.major, linked.minor, linked.patch); + * \endcode + * + * This function may be called safely at any time, even before Sound_Init(). + * + * \param ver Sound_Version structure to fill with shared library's version. + * + * \sa Sound_Version + * \sa SOUND_VERSION + */ +SNDDECLSPEC void SDLCALL Sound_GetLinkedVersion(Sound_Version *ver); + + +/** + * \fn Sound_Init(void) + * \brief Initialize SDL_sound. + * + * This must be called before any other SDL_sound function (except perhaps + * Sound_GetLinkedVersion()). You should call SDL_Init() before calling this. + * Sound_Init() will attempt to call SDL_Init(SDL_INIT_AUDIO), just in case. + * This is a safe behaviour, but it may not configure SDL to your liking by + * itself. + * + * \return nonzero on success, zero on error. Specifics of the + * error can be gleaned from Sound_GetError(). + * + * \sa Sound_Quit + */ +SNDDECLSPEC int SDLCALL Sound_Init(void); + + +/** + * \fn Sound_Quit(void) + * \brief Shutdown SDL_sound. + * + * This closes any SDL_RWops that were being used as sound sources, and frees + * any resources in use by SDL_sound. + * + * All Sound_Sample pointers you had prior to this call are INVALIDATED. + * + * Once successfully deinitialized, Sound_Init() can be called again to + * restart the subsystem. All default API states are restored at this + * point. + * + * You should call this BEFORE SDL_Quit(). This will NOT call SDL_Quit() + * for you! + * + * \return nonzero on success, zero on error. Specifics of the error + * can be gleaned from Sound_GetError(). If failure, state of + * SDL_sound is undefined, and probably badly screwed up. + * + * \sa Sound_Init + */ +SNDDECLSPEC int SDLCALL Sound_Quit(void); + + +/** + * \fn const Sound_DecoderInfo **Sound_AvailableDecoders(void) + * \brief Get a list of sound formats supported by this version of SDL_sound. + * + * This is for informational purposes only. Note that the extension listed is + * merely convention: if we list "MP3", you can open an MPEG-1 Layer 3 audio + * file with an extension of "XYZ", if you like. The file extensions are + * informational, and only required as a hint to choosing the correct + * decoder, since the sound data may not be coming from a file at all, thanks + * to the abstraction that an SDL_RWops provides. + * + * The returned value is an array of pointers to Sound_DecoderInfo structures, + * with a NULL entry to signify the end of the list: + * + * \code + * Sound_DecoderInfo **i; + * + * for (i = Sound_AvailableDecoders(); *i != NULL; i++) + * { + * printf("Supported sound format: [%s], which is [%s].\n", + * i->extension, i->description); + * // ...and other fields... + * } + * \endcode + * + * The return values are pointers to static internal memory, and should + * be considered READ ONLY, and never freed. + * + * \return READ ONLY Null-terminated array of READ ONLY structures. + * + * \sa Sound_DecoderInfo + */ +SNDDECLSPEC const Sound_DecoderInfo ** SDLCALL Sound_AvailableDecoders(void); + + +/** + * \fn const char *Sound_GetError(void) + * \brief Get the last SDL_sound error message as a null-terminated string. + * + * This will be NULL if there's been no error since the last call to this + * function. The pointer returned by this call points to an internal buffer, + * and should not be deallocated. Each thread has a unique error state + * associated with it, but each time a new error message is set, it will + * overwrite the previous one associated with that thread. It is safe to call + * this function at anytime, even before Sound_Init(). + * + * \return READ ONLY string of last error message. + * + * \sa Sound_ClearError + */ +SNDDECLSPEC const char * SDLCALL Sound_GetError(void); + + +/** + * \fn void Sound_ClearError(void) + * \brief Clear the current error message. + * + * The next call to Sound_GetError() after Sound_ClearError() will return NULL. + * + * \sa Sound_GetError + */ +SNDDECLSPEC void SDLCALL Sound_ClearError(void); + + +/** + * \fn Sound_Sample *Sound_NewSample(SDL_RWops *rw, const char *ext, Sound_AudioInfo *desired, Uint32 bufferSize) + * \brief Start decoding a new sound sample. + * + * The data is read via an SDL_RWops structure (see SDL_rwops.h in the SDL + * include directory), so it may be coming from memory, disk, network stream, + * etc. The (ext) parameter is merely a hint to determining the correct + * decoder; if you specify, for example, "mp3" for an extension, and one of + * the decoders lists that as a handled extension, then that decoder is given + * first shot at trying to claim the data for decoding. If none of the + * extensions match (or the extension is NULL), then every decoder examines + * the data to determine if it can handle it, until one accepts it. In such a + * case your SDL_RWops will need to be capable of rewinding to the start of + * the stream. + * + * If no decoders can handle the data, a NULL value is returned, and a human + * readable error message can be fetched from Sound_GetError(). + * + * Optionally, a desired audio format can be specified. If the incoming data + * is in a different format, SDL_sound will convert it to the desired format + * on the fly. Note that this can be an expensive operation, so it may be + * wise to convert data before you need to play it back, if possible, or + * make sure your data is initially in the format that you need it in. + * If you don't want to convert the data, you can specify NULL for a desired + * format. The incoming format of the data, preconversion, can be found + * in the Sound_Sample structure. + * + * Note that the raw sound data "decoder" needs you to specify both the + * extension "RAW" and a "desired" format, or it will refuse to handle + * the data. This is to prevent it from catching all formats unsupported + * by the other decoders. + * + * Finally, specify an initial buffer size; this is the number of bytes that + * will be allocated to store each read from the sound buffer. The more you + * can safely allocate, the more decoding can be done in one block, but the + * more resources you have to use up, and the longer each decoding call will + * take. Note that different data formats require more or less space to + * store. This buffer can be resized via Sound_SetBufferSize() ... + * + * The buffer size specified must be a multiple of the size of a single + * sample point. So, if you want 16-bit, stereo samples, then your sample + * point size is (2 channels * 16 bits), or 32 bits per sample, which is four + * bytes. In such a case, you could specify 128 or 132 bytes for a buffer, + * but not 129, 130, or 131 (although in reality, you'll want to specify a + * MUCH larger buffer). + * + * When you are done with this Sound_Sample pointer, you can dispose of it + * via Sound_FreeSample(). + * + * You do not have to keep a reference to (rw) around. If this function + * suceeds, it stores (rw) internally (and disposes of it during the call + * to Sound_FreeSample()). If this function fails, it will dispose of the + * SDL_RWops for you. + * + * \param rw SDL_RWops with sound data. + * \param ext File extension normally associated with a data format. + * Can usually be NULL. + * \param desired Format to convert sound data into. Can usually be NULL, + * if you don't need conversion. + * \param bufferSize Size, in bytes, to allocate for the decoding buffer. + * \return Sound_Sample pointer, which is used as a handle to several other + * SDL_sound APIs. NULL on error. If error, use + * Sound_GetError() to see what went wrong. + * + * \sa Sound_NewSampleFromFile + * \sa Sound_SetBufferSize + * \sa Sound_Decode + * \sa Sound_DecodeAll + * \sa Sound_Seek + * \sa Sound_Rewind + * \sa Sound_FreeSample + */ +SNDDECLSPEC Sound_Sample * SDLCALL Sound_NewSample(SDL_RWops *rw, + const char *ext, + Sound_AudioInfo *desired, + Uint32 bufferSize); + +/** + * \fn Sound_Sample *Sound_NewSampleFromMem(const Uint8 *data, Uint32 size, const char *ext, Sound_AudioInfo *desired, Uint32 bufferSize) + * \brief Start decoding a new sound sample from a file on disk. + * + * This is identical to Sound_NewSample(), but it creates an SDL_RWops for you + * from the (size) bytes of memory referenced by (data). + * + * This can pool RWops structures, so it may fragment the heap less over time + * than using SDL_RWFromMem(). + * + * \param data Buffer of data holding contents of an audio file to decode. + * \param size Size, in bytes, of buffer pointed to by (data). + * \param ext File extension normally associated with a data format. + * Can usually be NULL. + * \param desired Format to convert sound data into. Can usually be NULL, + * if you don't need conversion. + * \param bufferSize size, in bytes, of initial read buffer. + * \return Sound_Sample pointer, which is used as a handle to several other + * SDL_sound APIs. NULL on error. If error, use + * Sound_GetError() to see what went wrong. + * + * \sa Sound_NewSample + * \sa Sound_SetBufferSize + * \sa Sound_Decode + * \sa Sound_DecodeAll + * \sa Sound_Seek + * \sa Sound_Rewind + * \sa Sound_FreeSample + */ +SNDDECLSPEC Sound_Sample * SDLCALL Sound_NewSampleFromMem(const Uint8 *data, + Uint32 size, + const char *ext, + Sound_AudioInfo *desired, + Uint32 bufferSize); + + +/** + * \fn Sound_Sample *Sound_NewSampleFromFile(const char *filename, Sound_AudioInfo *desired, Uint32 bufferSize) + * \brief Start decoding a new sound sample from a file on disk. + * + * This is identical to Sound_NewSample(), but it creates an SDL_RWops for you + * from the file located in (filename). Note that (filename) is specified in + * platform-dependent notation. ("C:\\music\\mysong.mp3" on windows, and + * "/home/icculus/music/mysong.mp3" or whatever on Unix, etc.) + * Sound_NewSample()'s "ext" parameter is gleaned from the contents of + * (filename). + * + * This can pool RWops structures, so it may fragment the heap less over time + * than using SDL_RWFromFile(). + * + * \param filename file containing sound data. + * \param desired Format to convert sound data into. Can usually be NULL, + * if you don't need conversion. + * \param bufferSize size, in bytes, of initial read buffer. + * \return Sound_Sample pointer, which is used as a handle to several other + * SDL_sound APIs. NULL on error. If error, use + * Sound_GetError() to see what went wrong. + * + * \sa Sound_NewSample + * \sa Sound_SetBufferSize + * \sa Sound_Decode + * \sa Sound_DecodeAll + * \sa Sound_Seek + * \sa Sound_Rewind + * \sa Sound_FreeSample + */ +SNDDECLSPEC Sound_Sample * SDLCALL Sound_NewSampleFromFile(const char *fname, + Sound_AudioInfo *desired, + Uint32 bufferSize); + +/** + * \fn void Sound_FreeSample(Sound_Sample *sample) + * \brief Dispose of a Sound_Sample. + * + * This will also close/dispose of the SDL_RWops that was used at creation + * time, so there's no need to keep a reference to that around. + * The Sound_Sample pointer is invalid after this call, and will almost + * certainly result in a crash if you attempt to keep using it. + * + * \param sample The Sound_Sample to delete. + * + * \sa Sound_NewSample + * \sa Sound_NewSampleFromFile + */ +SNDDECLSPEC void SDLCALL Sound_FreeSample(Sound_Sample *sample); + + +/** + * \fn Sint32 Sound_GetDuration(Sound_Sample *sample) + * \brief Retrieve total play time of sample, in milliseconds. + * + * Report total time length of sample, in milliseconds. This is a fast + * call. Duration is calculated during Sound_NewSample*, so this is just + * an accessor into otherwise opaque data. + * + * Please note that not all formats can determine a total time, some can't + * be exact without fully decoding the data, and thus will estimate the + * duration. Many decoders will require the ability to seek in the data + * stream to calculate this, so even if we can tell you how long an .ogg + * file will be, the same data set may fail if it's, say, streamed over an + * HTTP connection. Plan accordingly. + * + * Most people won't need this function to just decode and playback, but it + * can be useful for informational purposes in, say, a music player's UI. + * + * \param sample Sound_Sample from which to retrieve duration information. + * \return Sample length in milliseconds, or -1 if duration can't be + * determined for any reason. + */ +SNDDECLSPEC Sint32 SDLCALL Sound_GetDuration(Sound_Sample *sample); + + +/** + * \fn int Sound_SetBufferSize(Sound_Sample *sample, Uint32 new_size) + * \brief Change the current buffer size for a sample. + * + * If the buffer size could be changed, then the sample->buffer and + * sample->buffer_size fields will reflect that. If they could not be + * changed, then your original sample state is preserved. If the buffer is + * shrinking, the data at the end of buffer is truncated. If the buffer is + * growing, the contents of the new space at the end is undefined until you + * decode more into it or initialize it yourself. + * + * The buffer size specified must be a multiple of the size of a single + * sample point. So, if you want 16-bit, stereo samples, then your sample + * point size is (2 channels * 16 bits), or 32 bits per sample, which is four + * bytes. In such a case, you could specify 128 or 132 bytes for a buffer, + * but not 129, 130, or 131 (although in reality, you'll want to specify a + * MUCH larger buffer). + * + * \param sample The Sound_Sample whose buffer to modify. + * \param new_size The desired size, in bytes, of the new buffer. + * \return non-zero if buffer size changed, zero on failure. + * + * \sa Sound_Decode + * \sa Sound_DecodeAll + */ +SNDDECLSPEC int SDLCALL Sound_SetBufferSize(Sound_Sample *sample, + Uint32 new_size); + + +/** + * \fn Uint32 Sound_Decode(Sound_Sample *sample) + * \brief Decode more of the sound data in a Sound_Sample. + * + * It will decode at most sample->buffer_size bytes into sample->buffer in the + * desired format, and return the number of decoded bytes. + * If sample->buffer_size bytes could not be decoded, then please refer to + * sample->flags to determine if this was an end-of-stream or error condition. + * + * \param sample Do more decoding to this Sound_Sample. + * \return number of bytes decoded into sample->buffer. If it is less than + * sample->buffer_size, then you should check sample->flags to see + * what the current state of the sample is (EOF, error, read again). + * + * \sa Sound_DecodeAll + * \sa Sound_SetBufferSize + * \sa Sound_Seek + * \sa Sound_Rewind + */ +SNDDECLSPEC Uint32 SDLCALL Sound_Decode(Sound_Sample *sample); + + +/** + * \fn Uint32 Sound_DecodeAll(Sound_Sample *sample) + * \brief Decode the remainder of the sound data in a Sound_Sample. + * + * This will dynamically allocate memory for the ENTIRE remaining sample. + * sample->buffer_size and sample->buffer will be updated to reflect the + * new buffer. Please refer to sample->flags to determine if the decoding + * finished due to an End-of-stream or error condition. + * + * Be aware that sound data can take a large amount of memory, and that + * this function may block for quite awhile while processing. Also note + * that a streaming source (for example, from a SDL_RWops that is getting + * fed from an Internet radio feed that doesn't end) may fill all available + * memory before giving up...be sure to use this on finite sound sources + * only! + * + * When decoding the sample in its entirety, the work is done one buffer at a + * time. That is, sound is decoded in sample->buffer_size blocks, and + * appended to a continually-growing buffer until the decoding completes. + * That means that this function will need enough RAM to hold approximately + * sample->buffer_size bytes plus the complete decoded sample at most. The + * larger your buffer size, the less overhead this function needs, but beware + * the possibility of paging to disk. Best to make this user-configurable if + * the sample isn't specific and small. + * + * \param sample Do all decoding for this Sound_Sample. + * \return number of bytes decoded into sample->buffer. You should check + * sample->flags to see what the current state of the sample is + * (EOF, error, read again). + * + * \sa Sound_Decode + * \sa Sound_SetBufferSize + */ +SNDDECLSPEC Uint32 SDLCALL Sound_DecodeAll(Sound_Sample *sample); + + +/** + * \fn int Sound_Rewind(Sound_Sample *sample) + * \brief Rewind a sample to the start. + * + * Restart a sample at the start of its waveform data, as if newly + * created with Sound_NewSample(). If successful, the next call to + * Sound_Decode[All]() will give audio data from the earliest point + * in the stream. + * + * Beware that this function will fail if the SDL_RWops that feeds the + * decoder can not be rewound via it's seek method, but this can + * theoretically be avoided by wrapping it in some sort of buffering + * SDL_RWops. + * + * This function should ONLY fail if the RWops is not seekable, or + * SDL_sound is not initialized. Both can be controlled by the application, + * and thus, it is up to the developer's paranoia to dictate whether this + * function's return value need be checked at all. + * + * If this function fails, the state of the sample is undefined, but it + * is still safe to call Sound_FreeSample() to dispose of it. + * + * On success, ERROR, EOF, and EAGAIN are cleared from sample->flags. The + * ERROR flag is set on error. + * + * \param sample The Sound_Sample to rewind. + * \return nonzero on success, zero on error. Specifics of the + * error can be gleaned from Sound_GetError(). + * + * \sa Sound_Seek + */ +SNDDECLSPEC int SDLCALL Sound_Rewind(Sound_Sample *sample); + + +/** + * \fn int Sound_Seek(Sound_Sample *sample, Uint32 ms) + * \brief Seek to a different point in a sample. + * + * Reposition a sample's stream. If successful, the next call to + * Sound_Decode[All]() will give audio data from the offset you + * specified. + * + * The offset is specified in milliseconds from the start of the + * sample. + * + * Beware that this function can fail for several reasons. If the + * SDL_RWops that feeds the decoder can not seek, this call will almost + * certainly fail, but this can theoretically be avoided by wrapping it + * in some sort of buffering SDL_RWops. Some decoders can never seek, + * others can only seek with certain files. The decoders will set a flag + * in the sample at creation time to help you determine this. + * + * You should check sample->flags & SOUND_SAMPLEFLAG_CANSEEK + * before attempting. Sound_Seek() reports failure immediately if this + * flag isn't set. This function can still fail for other reasons if the + * flag is set. + * + * This function can be emulated in the application with Sound_Rewind() + * and predecoding a specific amount of the sample, but this can be + * extremely inefficient. Sound_Seek() accelerates the seek on a + * with decoder-specific code. + * + * If this function fails, the sample should continue to function as if + * this call was never made. If there was an unrecoverable error, + * sample->flags & SOUND_SAMPLEFLAG_ERROR will be set, which you regular + * decoding loop can pick up. + * + * On success, ERROR, EOF, and EAGAIN are cleared from sample->flags. + * + * \param sample The Sound_Sample to seek. + * \param ms The new position, in milliseconds from start of sample. + * \return nonzero on success, zero on error. Specifics of the + * error can be gleaned from Sound_GetError(). + * + * \sa Sound_Rewind + */ +SNDDECLSPEC int SDLCALL Sound_Seek(Sound_Sample *sample, Uint32 ms); + +#ifdef __cplusplus +} +#endif + +#endif /* !defined _INCLUDE_SDL_SOUND_H_ */ + +/* end of SDL_sound.h ... */ + diff --git a/SDL2_sound/SDL_sound_aiff.c b/SDL2_sound/SDL_sound_aiff.c new file mode 100644 index 00000000..00e82896 --- /dev/null +++ b/SDL2_sound/SDL_sound_aiff.c @@ -0,0 +1,537 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Torbjörn Andersson. + */ + +/* + * AIFF decoder for SDL_sound + * + * [Insert something profound about the AIFF file format here.] + * + * This code was ripped from a decoder I had written for SDL_mixer, which was + * based on SDL_mixer's old AIFF music loader. (This loader was unfortunately + * completely broken, but it was still useful because all the pieces were + * still there, so to speak.) + * + * When rewriting it for SDL_sound, I changed its structure to be more like + * the WAV loader Ryan wrote. Had they not both been part of the same project + * it would have been embarrassing how similar they are. + * + * It is not the most feature-complete AIFF loader the world has ever seen. + * For instance, it only makes a token attempt at implementing the AIFF-C + * standard; basically the parts of it that I can easily understand and test. + * It's a start, though. + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_AIFF + +/***************************************************************************** + * aiff_t is what we store in our internal->decoder_private field... * + *****************************************************************************/ +typedef struct S_AIFF_FMT_T +{ + Uint32 type; + + Uint32 total_bytes; + Uint32 data_starting_offset; + + void (*free)(struct S_AIFF_FMT_T *fmt); + Uint32 (*read_sample)(Sound_Sample *sample); + int (*rewind_sample)(Sound_Sample *sample); + int (*seek_sample)(Sound_Sample *sample, Uint32 ms); + + +#if 0 +/* + this is ripped from wav.c as ann example of format-specific data. + please replace with something more appropriate when the need arises. +*/ + union + { + struct + { + Uint16 cbSize; + Uint16 wSamplesPerBlock; + Uint16 wNumCoef; + ADPCMCOEFSET *aCoeff; + } adpcm; + + /* put other format-specific data here... */ + } fmt; +#endif +} fmt_t; + + +typedef struct +{ + fmt_t fmt; + Sint32 bytesLeft; +} aiff_t; + + + + /* Chunk management code... */ + +#define formID 0x4D524F46 /* "FORM", in ascii. */ +#define aiffID 0x46464941 /* "AIFF", in ascii. */ +#define aifcID 0x43464941 /* "AIFC", in ascii. */ +#define ssndID 0x444E5353 /* "SSND", in ascii. */ + + +/***************************************************************************** + * The COMM chunk... * + *****************************************************************************/ + +#define commID 0x4D4D4F43 /* "COMM", in ascii. */ + +/* format/compression types... */ +#define noneID 0x454E4F4E /* "NONE", in ascii. */ + +typedef struct +{ + Uint32 ckID; + Uint32 ckDataSize; + Uint16 numChannels; + Uint32 numSampleFrames; + Uint16 sampleSize; + Uint32 sampleRate; + /* + * We don't handle AIFF-C compressed audio yet, but for those + * interested the allowed compression types are supposed to be + * + * compressionType compressionName meaning + * --------------------------------------------------------------- + * 'NONE' "not compressed" uncompressed, that is, + * straight digitized samples + * 'ACE2' "ACE 2-to-1" 2-to-1 IIGS ACE (Audio + * Compression / Expansion) + * 'ACE8' "ACE 8-to-3" 8-to-3 IIGS ACE (Audio + * Compression / Expansion) + * 'MAC3' "MACE 3-to-1" 3-to-1 Macintosh Audio + * Compression / Expansion + * 'MAC6' "MACE 6-to-1" 6-to-1 Macintosh Audio + * Compression / Expansion + * + * A pstring is a "Pascal-style string", that is, "one byte followed + * by test bytes followed when needed by one pad byte. The total + * number of bytes in a pstring must be even. The pad byte is + * included when the number of text bytes is even, so the total of + * text bytes + one count byte + one pad byte will be even. This pad + * byte is not reflected in the count." + * + * As for how these compression algorithms work, your guess is as + * good as mine. + */ + Uint32 compressionType; +#if 0 + pstring compressionName; +#endif +} comm_t; + + +/* + * Sample rate is encoded as an "80 bit IEEE Standard 754 floating point + * number (Standard Apple Numeric Environment [SANE] data type Extended)". + * Whose bright idea was that? + * + * This function was adapted from libsndfile, and while I do know a little + * bit about the IEEE floating point standard I don't pretend to fully + * understand this. + */ +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]); +} /* SANE_to_Uint32 */ + + +/* + * Read in a comm_t from disk. This makes this process safe regardless of + * the processor's byte order or how the comm_t structure is packed. + */ +static int read_comm_chunk(SDL_RWops *rw, comm_t *comm) +{ + Uint8 sampleRate[10]; + + /* skip reading the chunk ID, since it was already read at this point... */ + comm->ckID = commID; + + if (SDL_RWread(rw, &comm->ckDataSize, sizeof (comm->ckDataSize), 1) != 1) + return 0; + comm->ckDataSize = SDL_SwapBE32(comm->ckDataSize); + + if (SDL_RWread(rw, &comm->numChannels, sizeof (comm->numChannels), 1) != 1) + return 0; + comm->numChannels = SDL_SwapBE16(comm->numChannels); + + if (SDL_RWread(rw, &comm->numSampleFrames, + sizeof (comm->numSampleFrames), 1) != 1) + return 0; + comm->numSampleFrames = SDL_SwapBE32(comm->numSampleFrames); + + if (SDL_RWread(rw, &comm->sampleSize, sizeof (comm->sampleSize), 1) != 1) + return 0; + comm->sampleSize = SDL_SwapBE16(comm->sampleSize); + + if (SDL_RWread(rw, sampleRate, sizeof (sampleRate), 1) != 1) + return 0; + comm->sampleRate = SANE_to_Uint32(sampleRate); + + if (comm->ckDataSize > sizeof(comm->numChannels) + + sizeof(comm->numSampleFrames) + + sizeof(comm->sampleSize) + + sizeof(sampleRate)) + { + if (SDL_RWread(rw, &comm->compressionType, + sizeof (comm->compressionType), 1) != 1) + return 0; + comm->compressionType = SDL_SwapBE32(comm->compressionType); + } /* if */ + else + { + comm->compressionType = noneID; + } /* else */ + + return 1; +} /* read_comm_chunk */ + + + +/***************************************************************************** + * The SSND chunk... * + *****************************************************************************/ + +typedef struct +{ + Uint32 ckID; + Uint32 ckDataSize; + Uint32 offset; + Uint32 blockSize; + /* + * Then, comm->numSampleFrames sample frames. (It's better to get the + * length from numSampleFrames than from ckDataSize.) + */ +} ssnd_t; + + +static int read_ssnd_chunk(SDL_RWops *rw, ssnd_t *ssnd) +{ + /* skip reading the chunk ID, since it was already read at this point... */ + ssnd->ckID = ssndID; + + if (SDL_RWread(rw, &ssnd->ckDataSize, sizeof (ssnd->ckDataSize), 1) != 1) + return 0; + ssnd->ckDataSize = SDL_SwapBE32(ssnd->ckDataSize); + + if (SDL_RWread(rw, &ssnd->offset, sizeof (ssnd->offset), 1) != 1) + return 0; + ssnd->offset = SDL_SwapBE32(ssnd->offset); + + if (SDL_RWread(rw, &ssnd->blockSize, sizeof (ssnd->blockSize), 1) != 1) + return 0; + ssnd->blockSize = SDL_SwapBE32(ssnd->blockSize); + + /* Leave the SDL_RWops position indicator at the start of the samples */ + if (SDL_RWseek(rw, (int) ssnd->offset, SEEK_CUR) == -1) + return 0; + + return 1; +} /* read_ssnd_chunk */ + + + +/***************************************************************************** + * Normal, uncompressed aiff handler... * + *****************************************************************************/ + +static Uint32 read_sample_fmt_normal(Sound_Sample *sample) +{ + Uint32 retval; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + aiff_t *a = (aiff_t *) internal->decoder_private; + Uint32 max = (internal->buffer_size < (Uint32) a->bytesLeft) ? + internal->buffer_size : (Uint32) a->bytesLeft; + + SDL_assert(max > 0); + + /* + * We don't actually do any decoding, so we read the AIFF data + * directly into the internal buffer... + */ + retval = SDL_RWread(internal->rw, internal->buffer, 1, max); + + a->bytesLeft -= retval; + + /* Make sure the read went smoothly... */ + if ((retval == 0) || (a->bytesLeft == 0)) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + + else if (retval == -1) + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + + /* (next call this EAGAIN may turn into an EOF or error.) */ + else if (retval < internal->buffer_size) + sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + + return retval; +} /* read_sample_fmt_normal */ + + +static int rewind_sample_fmt_normal(Sound_Sample *sample) +{ + /* no-op. */ + return 1; +} /* rewind_sample_fmt_normal */ + + +static int seek_sample_fmt_normal(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + aiff_t *a = (aiff_t *) internal->decoder_private; + fmt_t *fmt = &a->fmt; + int offset = __Sound_convertMsToBytePos(&sample->actual, ms); + int pos = (int) (fmt->data_starting_offset + offset); + int rc = SDL_RWseek(internal->rw, pos, SEEK_SET); + BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0); + a->bytesLeft = fmt->total_bytes - offset; + return 1; /* success. */ +} /* seek_sample_fmt_normal */ + + +static void free_fmt_normal(fmt_t *fmt) +{ + /* it's a no-op. */ +} /* free_fmt_normal */ + + +static int read_fmt_normal(SDL_RWops *rw, fmt_t *fmt) +{ + /* (don't need to read more from the RWops...) */ + fmt->free = free_fmt_normal; + fmt->read_sample = read_sample_fmt_normal; + fmt->rewind_sample = rewind_sample_fmt_normal; + fmt->seek_sample = seek_sample_fmt_normal; + return 1; +} /* read_fmt_normal */ + + + + +/***************************************************************************** + * Everything else... * + *****************************************************************************/ + +static int AIFF_init(void) +{ + return 1; /* always succeeds. */ +} /* AIFF_init */ + + +static void AIFF_quit(void) +{ + /* it's a no-op. */ +} /* AIFF_quit */ + + +static int find_chunk(SDL_RWops *rw, Uint32 id) +{ + Sint32 siz = 0; + Uint32 _id = 0; + + while (1) + { + BAIL_IF_MACRO(SDL_RWread(rw, &_id, sizeof (_id), 1) != 1, NULL, 0); + if (SDL_SwapLE32(_id) == id) + return 1; + + BAIL_IF_MACRO(SDL_RWread(rw, &siz, sizeof (siz), 1) != 1, NULL, 0); + siz = SDL_SwapBE32(siz); + SDL_assert(siz > 0); + BAIL_IF_MACRO(SDL_RWseek(rw, siz, SEEK_CUR) == -1, NULL, 0); + } /* while */ + + return 0; /* shouldn't hit this, but just in case... */ +} /* find_chunk */ + + +static int read_fmt(SDL_RWops *rw, comm_t *c, fmt_t *fmt) +{ + fmt->type = c->compressionType; + + /* if it's in this switch statement, we support the format. */ + switch (fmt->type) + { + case noneID: + SNDDBG(("AIFF: Appears to be uncompressed audio.\n")); + return read_fmt_normal(rw, fmt); + + /* add other types here. */ + + default: + SNDDBG(("AIFF: Format %lu is unknown.\n", + (unsigned int) fmt->type)); + BAIL_MACRO("AIFF: Unsupported format", 0); + } /* switch */ + + SDL_assert(0); /* shouldn't hit this point. */ + return 0; +} /* read_fmt */ + + +static int AIFF_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + Uint32 chunk_id; + int bytes_per_sample; + long pos; + comm_t c; + ssnd_t s; + aiff_t *a; + + BAIL_IF_MACRO(SDL_ReadLE32(rw) != formID, "AIFF: Not a FORM file.", 0); + SDL_ReadBE32(rw); /* throw the length away; we don't need it. */ + + chunk_id = SDL_ReadLE32(rw); + BAIL_IF_MACRO(chunk_id != aiffID && chunk_id != aifcID, + "AIFF: Not an AIFF or AIFC file.", 0); + + /* Chunks may appear in any order, so we establish base camp here. */ + pos = SDL_RWtell(rw); + + BAIL_IF_MACRO(!find_chunk(rw, commID), "AIFF: No common chunk.", 0); + BAIL_IF_MACRO(!read_comm_chunk(rw, &c), + "AIFF: Can't read common chunk.", 0); + + sample->actual.channels = (Uint8) c.numChannels; + sample->actual.rate = c.sampleRate; + + /* Really, sample->total_time = (c.numSampleFrames*1000) c.sampleRate */ + internal->total_time = (c.numSampleFrames / c.sampleRate) * 1000; + internal->total_time += (c.numSampleFrames % c.sampleRate) + * 1000 / c.sampleRate; + + if (c.sampleSize <= 8) + { + sample->actual.format = AUDIO_S8; + bytes_per_sample = c.numChannels; + } /* if */ + else if (c.sampleSize <= 16) + { + sample->actual.format = AUDIO_S16MSB; + bytes_per_sample = 2 * c.numChannels; + } /* if */ + else + { + BAIL_MACRO("AIFF: Unsupported sample size.", 0); + } /* else */ + + BAIL_IF_MACRO(c.sampleRate == 0, "AIFF: Unsupported sample rate.", 0); + + a = (aiff_t *) SDL_malloc(sizeof(aiff_t)); + BAIL_IF_MACRO(a == NULL, ERR_OUT_OF_MEMORY, 0); + + if (!read_fmt(rw, &c, &(a->fmt))) + { + SDL_free(a); + return 0; + } /* if */ + + SDL_RWseek(rw, pos, SEEK_SET); /* if the seek fails, let it go... */ + + if (!find_chunk(rw, ssndID)) + { + SDL_free(a); + BAIL_MACRO("AIFF: No sound data chunk.", 0); + } /* if */ + + if (!read_ssnd_chunk(rw, &s)) + { + SDL_free(a); + BAIL_MACRO("AIFF: Can't read sound data chunk.", 0); + } /* if */ + + a->fmt.total_bytes = a->bytesLeft = bytes_per_sample * c.numSampleFrames; + a->fmt.data_starting_offset = SDL_RWtell(rw); + internal->decoder_private = (void *) a; + + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + + SNDDBG(("AIFF: Accepting data stream.\n")); + return 1; /* we'll handle this data. */ +} /* AIFF_open */ + + +static void AIFF_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + aiff_t *a = (aiff_t *) internal->decoder_private; + a->fmt.free(&(a->fmt)); + SDL_free(a); +} /* AIFF_close */ + + +static Uint32 AIFF_read(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + aiff_t *a = (aiff_t *) internal->decoder_private; + return a->fmt.read_sample(sample); +} /* AIFF_read */ + + +static int AIFF_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + aiff_t *a = (aiff_t *) internal->decoder_private; + fmt_t *fmt = &a->fmt; + int rc = SDL_RWseek(internal->rw, fmt->data_starting_offset, SEEK_SET); + BAIL_IF_MACRO(rc != fmt->data_starting_offset, ERR_IO_ERROR, 0); + a->bytesLeft = fmt->total_bytes; + return fmt->rewind_sample(sample); +} /* AIFF_rewind */ + + +static int AIFF_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + aiff_t *a = (aiff_t *) internal->decoder_private; + return a->fmt.seek_sample(sample, ms); +} /* AIFF_seek */ + +static const char *extensions_aiff[] = { "AIFF", "AIF", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_AIFF = +{ + { + extensions_aiff, + "Audio Interchange File Format", + "Torbjörn Andersson ", + "https://icculus.org/SDL_sound/" + }, + + AIFF_init, /* init() method */ + AIFF_quit, /* quit() method */ + AIFF_open, /* open() method */ + AIFF_close, /* close() method */ + AIFF_read, /* read() method */ + AIFF_rewind, /* rewind() method */ + AIFF_seek /* seek() method */ +}; + + +#endif /* SOUND_SUPPORTS_AIFF */ + +/* end of SDL_sound_aiff.c ... */ + diff --git a/SDL2_sound/SDL_sound_au.c b/SDL2_sound/SDL_sound_au.c new file mode 100644 index 00000000..0c78c566 --- /dev/null +++ b/SDL2_sound/SDL_sound_au.c @@ -0,0 +1,352 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Mattias Engdegård. + */ + +/* + * Sun/NeXT .au decoder for SDL_sound. + * Formats supported: 8 and 16 bit linear PCM, 8 bit µ-law. + * Files without valid header are assumed to be 8 bit µ-law, 8kHz, mono. + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_AU + +/* no init/deinit needed */ +static int AU_init(void) +{ + return 1; +} /* AU_init */ + +static void AU_quit(void) +{ + /* no-op. */ +} /* AU_quit */ + +struct au_file_hdr +{ + Uint32 magic; + Uint32 hdr_size; + Uint32 data_size; + Uint32 encoding; + Uint32 sample_rate; + Uint32 channels; +}; + +#define HDR_SIZE 24 + +enum +{ + AU_ENC_ULAW_8 = 1, /* 8-bit ISDN µ-law */ + AU_ENC_LINEAR_8 = 2, /* 8-bit linear PCM */ + AU_ENC_LINEAR_16 = 3, /* 16-bit linear PCM */ + + /* the rest are unsupported (I have never seen them in the wild) */ + AU_ENC_LINEAR_24 = 4, /* 24-bit linear PCM */ + AU_ENC_LINEAR_32 = 5, /* 32-bit linear PCM */ + AU_ENC_FLOAT = 6, /* 32-bit IEEE floating point */ + AU_ENC_DOUBLE = 7, /* 64-bit IEEE floating point */ + /* more Sun formats, not supported either */ + AU_ENC_ADPCM_G721 = 23, + AU_ENC_ADPCM_G722 = 24, + AU_ENC_ADPCM_G723_3 = 25, + AU_ENC_ADPCM_G723_5 = 26, + AU_ENC_ALAW_8 = 27 +}; + +struct audec +{ + Uint32 total; + Uint32 remaining; + Uint32 start_offset; + int encoding; +}; + + +/* + * Read in the AU header from disk. This makes this process safe + * regardless of the processor's byte order or how the au_file_hdr + * structure is packed. + */ +static int read_au_header(SDL_RWops *rw, struct au_file_hdr *hdr) +{ + if (SDL_RWread(rw, &hdr->magic, sizeof (hdr->magic), 1) != 1) + return 0; + hdr->magic = SDL_SwapBE32(hdr->magic); + + if (SDL_RWread(rw, &hdr->hdr_size, sizeof (hdr->hdr_size), 1) != 1) + return 0; + hdr->hdr_size = SDL_SwapBE32(hdr->hdr_size); + + if (SDL_RWread(rw, &hdr->data_size, sizeof (hdr->data_size), 1) != 1) + return 0; + hdr->data_size = SDL_SwapBE32(hdr->data_size); + + if (SDL_RWread(rw, &hdr->encoding, sizeof (hdr->encoding), 1) != 1) + return 0; + hdr->encoding = SDL_SwapBE32(hdr->encoding); + + if (SDL_RWread(rw, &hdr->sample_rate, sizeof (hdr->sample_rate), 1) != 1) + return 0; + hdr->sample_rate = SDL_SwapBE32(hdr->sample_rate); + + if (SDL_RWread(rw, &hdr->channels, sizeof (hdr->channels), 1) != 1) + return 0; + hdr->channels = SDL_SwapBE32(hdr->channels); + + return 1; +} /* read_au_header */ + + +#define AU_MAGIC 0x2E736E64 /* ".snd", in ASCII (bigendian number) */ + +static int AU_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = sample->opaque; + SDL_RWops *rw = internal->rw; + int skip, hsize, i, bytes_per_second; + struct au_file_hdr hdr; + struct audec *dec; + char c; + + /* read_au_header() will do byte order swapping. */ + BAIL_IF_MACRO(!read_au_header(rw, &hdr), "AU: bad header", 0); + + dec = SDL_malloc(sizeof *dec); + BAIL_IF_MACRO(dec == NULL, ERR_OUT_OF_MEMORY, 0); + internal->decoder_private = dec; + + if (hdr.magic == AU_MAGIC) + { + /* valid magic */ + dec->encoding = hdr.encoding; + switch(dec->encoding) + { + case AU_ENC_ULAW_8: + /* Convert 8-bit µ-law to 16-bit linear on the fly. This is + slightly wasteful if the audio driver must convert them + back, but µ-law only devices are rare (mostly _old_ Suns) */ + sample->actual.format = AUDIO_S16SYS; + break; + + case AU_ENC_LINEAR_8: + sample->actual.format = AUDIO_S8; + break; + + case AU_ENC_LINEAR_16: + sample->actual.format = AUDIO_S16MSB; + break; + + default: + SDL_free(dec); + BAIL_MACRO("AU: Unsupported .au encoding", 0); + } /* switch */ + + sample->actual.rate = hdr.sample_rate; + sample->actual.channels = hdr.channels; + dec->remaining = hdr.data_size; + hsize = hdr.hdr_size; + + /* skip remaining part of header (input may be unseekable) */ + for (i = HDR_SIZE; i < hsize; i++) + { + if (SDL_RWread(rw, &c, 1, 1) != 1) + { + SDL_free(dec); + BAIL_MACRO(ERR_IO_ERROR, 0); + } /* if */ + } /* for */ + } /* if */ + + else if (SDL_strcasecmp(ext, "au") == 0) + { + /* + * A number of files in the wild have the .au extension but no valid + * header; these are traditionally assumed to be 8kHz µ-law. Handle + * them here only if the extension is recognized. + */ + + SNDDBG(("AU: Invalid header, assuming raw 8kHz µ-law.\n")); + /* if seeking fails, we lose 24 samples. big deal */ + SDL_RWseek(rw, -HDR_SIZE, SEEK_CUR); + dec->encoding = AU_ENC_ULAW_8; + dec->remaining = (Uint32)-1; /* no limit */ + sample->actual.format = AUDIO_S16SYS; + sample->actual.rate = 8000; + sample->actual.channels = 1; + } /* else if */ + + else + { + SDL_free(dec); + BAIL_MACRO("AU: Not an .AU stream.", 0); + } /* else */ + + bytes_per_second = ( ( dec->encoding == AU_ENC_LINEAR_16 ) ? 2 : 1 ) + * sample->actual.rate * sample->actual.channels ; + internal->total_time = ((dec->remaining == -1) ? (-1) : + ( ( dec->remaining / bytes_per_second ) * 1000 ) + + ( ( dec->remaining % bytes_per_second ) * 1000 / + bytes_per_second ) ); + + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + dec->total = dec->remaining; + dec->start_offset = SDL_RWtell(rw); + + SNDDBG(("AU: Accepting data stream.\n")); + return 1; +} /* AU_open */ + + +static void AU_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = sample->opaque; + SDL_free(internal->decoder_private); +} /* AU_close */ + + +/* table to convert from µ-law encoding to signed 16-bit samples, + generated by a throwaway perl script */ +static Sint16 ulaw_to_linear[256] = { + -32124,-31100,-30076,-29052,-28028,-27004,-25980,-24956, + -23932,-22908,-21884,-20860,-19836,-18812,-17788,-16764, + -15996,-15484,-14972,-14460,-13948,-13436,-12924,-12412, + -11900,-11388,-10876,-10364, -9852, -9340, -8828, -8316, + -7932, -7676, -7420, -7164, -6908, -6652, -6396, -6140, + -5884, -5628, -5372, -5116, -4860, -4604, -4348, -4092, + -3900, -3772, -3644, -3516, -3388, -3260, -3132, -3004, + -2876, -2748, -2620, -2492, -2364, -2236, -2108, -1980, + -1884, -1820, -1756, -1692, -1628, -1564, -1500, -1436, + -1372, -1308, -1244, -1180, -1116, -1052, -988, -924, + -876, -844, -812, -780, -748, -716, -684, -652, + -620, -588, -556, -524, -492, -460, -428, -396, + -372, -356, -340, -324, -308, -292, -276, -260, + -244, -228, -212, -196, -180, -164, -148, -132, + -120, -112, -104, -96, -88, -80, -72, -64, + -56, -48, -40, -32, -24, -16, -8, 0, + 32124, 31100, 30076, 29052, 28028, 27004, 25980, 24956, + 23932, 22908, 21884, 20860, 19836, 18812, 17788, 16764, + 15996, 15484, 14972, 14460, 13948, 13436, 12924, 12412, + 11900, 11388, 10876, 10364, 9852, 9340, 8828, 8316, + 7932, 7676, 7420, 7164, 6908, 6652, 6396, 6140, + 5884, 5628, 5372, 5116, 4860, 4604, 4348, 4092, + 3900, 3772, 3644, 3516, 3388, 3260, 3132, 3004, + 2876, 2748, 2620, 2492, 2364, 2236, 2108, 1980, + 1884, 1820, 1756, 1692, 1628, 1564, 1500, 1436, + 1372, 1308, 1244, 1180, 1116, 1052, 988, 924, + 876, 844, 812, 780, 748, 716, 684, 652, + 620, 588, 556, 524, 492, 460, 428, 396, + 372, 356, 340, 324, 308, 292, 276, 260, + 244, 228, 212, 196, 180, 164, 148, 132, + 120, 112, 104, 96, 88, 80, 72, 64, + 56, 48, 40, 32, 24, 16, 8, 0 +}; + + +static Uint32 AU_read(Sound_Sample *sample) +{ + int ret; + Sound_SampleInternal *internal = sample->opaque; + struct audec *dec = internal->decoder_private; + int maxlen; + Uint8 *buf; + + maxlen = internal->buffer_size; + buf = internal->buffer; + if (dec->encoding == AU_ENC_ULAW_8) + { + /* We read µ-law samples into the second half of the buffer, so + we can expand them to 16-bit samples afterwards */ + maxlen >>= 1; + buf += maxlen; + } /* if */ + + if (maxlen > dec->remaining) + maxlen = dec->remaining; + ret = SDL_RWread(internal->rw, buf, 1, maxlen); + if (ret == 0) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + else if (ret == -1) + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + else + { + dec->remaining -= ret; + if (ret < maxlen) + sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + + if (dec->encoding == AU_ENC_ULAW_8) + { + int i; + Sint16 *dst = internal->buffer; + for (i = 0; i < ret; i++) + dst[i] = ulaw_to_linear[buf[i]]; + ret <<= 1; /* return twice as much as read */ + } /* if */ + } /* else */ + + return ret; +} /* AU_read */ + + +static int AU_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + struct audec *dec = (struct audec *) internal->decoder_private; + int rc = SDL_RWseek(internal->rw, dec->start_offset, SEEK_SET); + BAIL_IF_MACRO(rc != dec->start_offset, ERR_IO_ERROR, 0); + dec->remaining = dec->total; + return 1; +} /* AU_rewind */ + + +static int AU_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + struct audec *dec = (struct audec *) internal->decoder_private; + int offset = __Sound_convertMsToBytePos(&sample->actual, ms); + int rc; + int pos; + + if (dec->encoding == AU_ENC_ULAW_8) + offset >>= 1; /* halve the byte offset for compression. */ + + pos = (int) (dec->start_offset + offset); + rc = SDL_RWseek(internal->rw, pos, SEEK_SET); + BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0); + dec->remaining = dec->total - offset; + return 1; +} /* AU_seek */ + +/* + * Sometimes the extension ".snd" is used for these files (mostly on the NeXT), + * and the magic number comes from this. However it may clash with other + * formats and is somewhat of an anachronism, so only .au is used here. + */ +static const char *extensions_au[] = { "AU", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_AU = +{ + { + extensions_au, + "Sun/NeXT audio file format", + "Mattias EngdegÃ¥rd ", + "https://icculus.org/SDL_sound/" + }, + + AU_init, /* init() method */ + AU_quit, /* quit() method */ + AU_open, /* open() method */ + AU_close, /* close() method */ + AU_read, /* read() method */ + AU_rewind, /* rewind() method */ + AU_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_AU */ + +/* end of SDL_sound_au.c ... */ + diff --git a/SDL2_sound/SDL_sound_coreaudio.c b/SDL2_sound/SDL_sound_coreaudio.c new file mode 100644 index 00000000..bd6af924 --- /dev/null +++ b/SDL2_sound/SDL_sound_coreaudio.c @@ -0,0 +1,747 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Eric Wing. + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_COREAUDIO + +#include /* NULL */ +#include /* htonl */ +#include + +typedef struct CoreAudioFileContainer +{ + AudioFileID* audioFileID; + ExtAudioFileRef extAudioFileRef; + AudioStreamBasicDescription* outputFormat; +} CoreAudioFileContainer; + + +static int CoreAudio_init(void) +{ + return 1; /* always succeeds. */ +} /* CoreAudio_init */ + + +static void CoreAudio_quit(void) +{ + /* it's a no-op. */ +} /* CoreAudio_quit */ + +/* + https://developer.apple.com/library/ios/#documentation/MusicAudio/Reference/AudioFileConvertRef/Reference/reference.html + kAudioFileAIFFType = 'AIFF', + kAudioFileAIFCType = 'AIFC', + kAudioFileWAVEType = 'WAVE', + kAudioFileSoundDesigner2Type = 'Sd2f', + kAudioFileNextType = 'NeXT', + kAudioFileMP3Type = 'MPG3', + kAudioFileMP2Type = 'MPG2', + kAudioFileMP1Type = 'MPG1', + kAudioFileAC3Type = 'ac-3', + kAudioFileAAC_ADTSType = 'adts', + kAudioFileMPEG4Type = 'mp4f', + kAudioFileM4AType = 'm4af', + kAudioFileCAFType = 'caff', + kAudioFile3GPType = '3gpp', + kAudioFile3GP2Type = '3gp2', + kAudioFileAMRType = 'amrf' +*/ +static AudioFileTypeID CoreAudio_GetAudioTypeForExtension(const char* file_extension) +{ + if( (SDL_strcasecmp(file_extension, "aif") == 0) + || (SDL_strcasecmp(file_extension, "aiff") == 0) + || (SDL_strcasecmp(file_extension, "aifc") == 0) + ) + { + return kAudioFileAIFCType; + } + else if( (SDL_strcasecmp(file_extension, "wav") == 0) + || (SDL_strcasecmp(file_extension, "wave") == 0) + ) + { + return kAudioFileWAVEType; + } + else if( (SDL_strcasecmp(file_extension, "mp3") == 0) + ) + { + return kAudioFileMP3Type; + } + else if( (SDL_strcasecmp(file_extension, "mp4") == 0) + ) + { + return kAudioFileMPEG4Type; + } + else if( (SDL_strcasecmp(file_extension, "m4a") == 0) + ) + { + return kAudioFileM4AType; + } + else if( (SDL_strcasecmp(file_extension, "aac") == 0) + ) + { + return kAudioFileAAC_ADTSType; + } + else if( (SDL_strcasecmp(file_extension, "aac") == 0) + ) + { + return kAudioFileAAC_ADTSType; + } + else if( (SDL_strcasecmp(file_extension, "caf") == 0) + || (SDL_strcasecmp(file_extension, "caff") == 0) + ) + { + return kAudioFileCAFType; + } + else if( (SDL_strcasecmp(file_extension, "Sd2f") == 0) + || (SDL_strcasecmp(file_extension, "sd2") == 0) + ) + { + return kAudioFileSoundDesigner2Type; + } + else if( (SDL_strcasecmp(file_extension, "au") == 0) + || (SDL_strcasecmp(file_extension, "next") == 0) + ) + { + return kAudioFileNextType; + } + else if( (SDL_strcasecmp(file_extension, "mp2") == 0) + ) + { + return kAudioFileMP2Type; + } + else if( (SDL_strcasecmp(file_extension, "mp1") == 0) + ) + { + return kAudioFileMP1Type; + } + else if( (SDL_strcasecmp(file_extension, "ac3") == 0) + ) + { + return kAudioFileAC3Type; + } + else if( (SDL_strcasecmp(file_extension, "3gpp") == 0) + ) + { + return kAudioFile3GPType; + } + else if( (SDL_strcasecmp(file_extension, "3gp2") == 0) + ) + { + return kAudioFile3GP2Type; + } + else if( (SDL_strcasecmp(file_extension, "amrf") == 0) + || (SDL_strcasecmp(file_extension, "amr") == 0) + ) + { + return kAudioFileAMRType; + } + else if( (SDL_strcasecmp(file_extension, "ima4") == 0) + || (SDL_strcasecmp(file_extension, "ima") == 0) + ) + { + /* not sure about this one */ + return kAudioFileCAFType; + } + else + { + return 0; + } + +} + +static const char* CoreAudio_FourCCToString(int32_t error_code) +{ + static char return_string[16]; + uint32_t big_endian_code = htonl(error_code); + char* big_endian_str = (char*)&big_endian_code; + // see if it appears to be a 4-char-code + if(isprint(big_endian_str[0]) + && isprint(big_endian_str[1]) + && isprint(big_endian_str[2]) + && isprint (big_endian_str[3])) + { + return_string[0] = '\''; + return_string[1] = big_endian_str[0]; + return_string[2] = big_endian_str[1]; + return_string[3] = big_endian_str[2]; + return_string[4] = big_endian_str[3]; + return_string[5] = '\''; + return_string[6] = '\0'; + } + else if(error_code > -200000 && error_code < 200000) + { + // no, format it as an integer + snprintf(return_string, 16, "%d", error_code); + } + else + { + // no, format it as an integer but in hex + snprintf(return_string, 16, "0x%x", error_code); + } + return return_string; +} + + + +SInt64 CoreAudio_SizeCallback(void* inClientData) +{ + SDL_RWops* rw_ops = (SDL_RWops*)inClientData; + SInt64 current_position = SDL_RWtell(rw_ops); + SInt64 end_position = SDL_RWseek(rw_ops, 0, SEEK_END); + SDL_RWseek(rw_ops, current_position, SEEK_SET); +// fprintf(stderr, "CoreAudio_SizeCallback:%d\n", end_position); + + return end_position; +} + +OSStatus CoreAudio_ReadCallback( + void* inClientData, + SInt64 inPosition, + UInt32 requestCount, + void* data_buffer, + UInt32* actualCount +) +{ + SDL_RWops* rw_ops = (SDL_RWops*)inClientData; + SDL_RWseek(rw_ops, inPosition, SEEK_SET); + size_t bytes_actually_read = SDL_RWread(rw_ops, data_buffer, 1, requestCount); + // Not sure how to test for a read error with SDL_RWops +// fprintf(stderr, "CoreAudio_ReadCallback:%d, %d\n", requestCount, bytes_actually_read); + + *actualCount = bytes_actually_read; + return noErr; +} + + +static int CoreAudio_open(Sound_Sample *sample, const char *ext) +{ + CoreAudioFileContainer* core_audio_file_container; + AudioFileID* audio_file_id; + OSStatus error_result; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + AudioStreamBasicDescription actual_format; + AudioStreamBasicDescription output_format; + Float64 estimated_duration; + UInt32 format_size; + + + core_audio_file_container = (CoreAudioFileContainer*)SDL_malloc(sizeof(CoreAudioFileContainer)); + BAIL_IF_MACRO(core_audio_file_container == NULL, ERR_OUT_OF_MEMORY, 0); + + + audio_file_id = (AudioFileID*)SDL_malloc(sizeof(AudioFileID)); + BAIL_IF_MACRO(audio_file_id == NULL, ERR_OUT_OF_MEMORY, 0); + + error_result = AudioFileOpenWithCallbacks( + internal->rw, + CoreAudio_ReadCallback, + NULL, + CoreAudio_SizeCallback, + NULL, + CoreAudio_GetAudioTypeForExtension(ext), + audio_file_id + ); + if (error_result != noErr) + { + AudioFileClose(*audio_file_id); + SDL_free(audio_file_id); + SDL_free(core_audio_file_container); + SNDDBG(("Core Audio: can't grok data. reason: [%s].\n", CoreAudio_FourCCToString(error_result))); + BAIL_MACRO("Core Audio: Not valid audio data.", 0); + } /* if */ + + format_size = sizeof(actual_format); + error_result = AudioFileGetProperty( + *audio_file_id, + kAudioFilePropertyDataFormat, + &format_size, + &actual_format + ); + if (error_result != noErr) + { + AudioFileClose(*audio_file_id); + SDL_free(audio_file_id); + SDL_free(core_audio_file_container); + SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s]", CoreAudio_FourCCToString(error_result))); + BAIL_MACRO("Core Audio: Not valid audio data.", 0); + } /* if */ + + format_size = sizeof(estimated_duration); + error_result = AudioFileGetProperty( + *audio_file_id, + kAudioFilePropertyEstimatedDuration, + &format_size, + &estimated_duration + ); + if (error_result != noErr) + { + AudioFileClose(*audio_file_id); + SDL_free(audio_file_id); + SDL_free(core_audio_file_container); + SNDDBG(("Core Audio: AudioFileGetProperty failed. reason: [%s].\n", CoreAudio_FourCCToString(error_result))); + BAIL_MACRO("Core Audio: Not valid audio data.", 0); + } /* if */ + + + core_audio_file_container->audioFileID = audio_file_id; + + internal->decoder_private = core_audio_file_container; + + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + sample->actual.rate = (UInt32) actual_format.mSampleRate; + sample->actual.channels = (UInt8)actual_format.mChannelsPerFrame; + internal->total_time = (SInt32)(estimated_duration * 1000.0 + 0.5); + +#if 0 + /* FIXME: Both Core Audio and SDL 1.3 support float and 32-bit formats */ + if(actual_format.mFormatFlags & kAudioFormatFlagIsBigEndian) + { + if(16 == actual_format.mBitsPerChannel) + { + if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) + { + sample->actual.format = AUDIO_S16MSB; + } + else + { + sample->actual.format = AUDIO_U16MSB; + } + } + else if(8 == actual_format.mBitsPerChannel) + { + if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) + { + sample->actual.format = AUDIO_S8; + } + else + { + sample->actual.format = AUDIO_U8; + } + } + else // might be 0 for undefined? + { + // This case seems to come up a lot for me. Maybe for file types like .m4a? + sample->actual.format = AUDIO_S16SYS; + SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel)); + + } + } + else // little endian + { + if(16 == actual_format.mBitsPerChannel) + { + if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) + { + sample->actual.format = AUDIO_S16LSB; + } + else + { + sample->actual.format = AUDIO_U16LSB; + } + } + else if(8 == actual_format.mBitsPerChannel) + { + if(kAudioFormatFlagIsSignedInteger & actual_format.mFormatFlags) + { + sample->actual.format = AUDIO_S8; + } + else + { + sample->actual.format = AUDIO_U8; + } + } + else // might be 0 for undefined? + { + sample->actual.format = AUDIO_S16SYS; + + SNDDBG(("Core Audio: Unsupported actual_format.mBitsPerChannel: [%d].\n", actual_format.mBitsPerChannel)); + } + + } +#else + + + + /* + * I want to use Core Audio to do conversion and decoding for performance reasons. + * This is particularly important on mobile devices like iOS. + * Taking from the Ogg Vorbis decode, I pretend the "actual" format is the same + * as the desired format. + */ + sample->actual.format = (sample->desired.format == 0) ? + AUDIO_S16SYS : sample->desired.format; +#endif + + + SNDDBG(("CoreAudio: channels == (%d).\n", sample->actual.channels)); + SNDDBG(("CoreAudio: sampling rate == (%d).\n",sample->actual.rate)); + SNDDBG(("CoreAudio: total seconds of sample == (%d).\n", internal->total_time)); + SNDDBG(("CoreAudio: sample->actual.format == (%d).\n", sample->actual.format)); + + + + error_result = ExtAudioFileWrapAudioFileID(*audio_file_id, + false, // set to false for read-only + &core_audio_file_container->extAudioFileRef + ); + if(error_result != noErr) + { + AudioFileClose(*audio_file_id); + SDL_free(audio_file_id); + SDL_free(core_audio_file_container); + SNDDBG(("Core Audio: can't wrap data. reason: [%s].\n", CoreAudio_FourCCToString(error_result))); + BAIL_MACRO("Core Audio: Failed to wrap data.", 0); + } /* if */ + + + /* The output format must be linear PCM because that's the only type OpenAL knows how to deal with. + * Set the client format to 16 bit signed integer (native-endian) data because that is the most + * optimal format on iPhone/iPod Touch hardware. + * Maintain the channel count and sample rate of the original source format. + */ + output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate + output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels + output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data + output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet + + if(sample->desired.format == 0) + { + // do AUDIO_S16SYS + output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; // I seem to read failures problems without kAudioFormatFlagIsPacked. From a mailing list post, this seems to be a Core Audio bug. + output_format.mBitsPerChannel = 16; // We know we want 16-bit + } + else + { + output_format.mFormatFlags = 0; // clear flags + output_format.mFormatFlags |= kAudioFormatFlagIsPacked; // I seem to read failures problems without kAudioFormatFlagIsPacked. From a mailing list post, this seems to be a Core Audio bug. + // Mask against bitsize + if(0xFF & sample->desired.format) + { + output_format.mBitsPerChannel = 16; /* 16-bit */ + } + else + { + output_format.mBitsPerChannel = 8; /* 8-bit */ + } + + // Mask for signed/unsigned + if((1<<15) & sample->desired.format) + { + output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsSignedInteger; + + } + else + { + // no flag set for unsigned + } + // Mask for big/little endian + if((1<<12) & sample->desired.format) + { + output_format.mFormatFlags = output_format.mFormatFlags | kAudioFormatFlagIsBigEndian; + } + else + { + // no flag set for little endian + } + } + + output_format.mBytesPerPacket = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // e.g. 16-bits/8 * channels => so 2-bytes per channel per frame + output_format.mBytesPerFrame = output_format.mBitsPerChannel/8 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket + + +/* + output_format.mSampleRate = actual_format.mSampleRate; // preserve the original sample rate + output_format.mChannelsPerFrame = actual_format.mChannelsPerFrame; // preserve the number of channels + output_format.mFormatID = kAudioFormatLinearPCM; // We want linear PCM data +// output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; + output_format.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger; + output_format.mFramesPerPacket = 1; // We know for linear PCM, the definition is 1 frame per packet + output_format.mBitsPerChannel = 16; // We know we want 16-bit + output_format.mBytesPerPacket = 2 * output_format.mChannelsPerFrame; // We know we are using 16-bit, so 2-bytes per channel per frame + output_format.mBytesPerFrame = 2 * output_format.mChannelsPerFrame; // For PCM, since 1 frame is 1 packet, it is the same as mBytesPerPacket +*/ + SNDDBG(("output_format: mSampleRate: %lf\n", output_format.mSampleRate)); + SNDDBG(("output_format: mChannelsPerFrame: %d\n", output_format.mChannelsPerFrame)); + SNDDBG(("output_format: mFormatID: %d\n", output_format.mFormatID)); + SNDDBG(("output_format: mFormatFlags: %d\n", output_format.mFormatFlags)); + SNDDBG(("output_format: mFramesPerPacket: %d\n", output_format.mFramesPerPacket)); + SNDDBG(("output_format: mBitsPerChannel: %d\n", output_format.mBitsPerChannel)); + SNDDBG(("output_format: mBytesPerPacket: %d\n", output_format.mBytesPerPacket)); + SNDDBG(("output_format: mBytesPerFrame: %d\n", output_format.mBytesPerFrame)); + + + /* Set the desired client (output) data format */ + error_result = ExtAudioFileSetProperty(core_audio_file_container->extAudioFileRef, kExtAudioFileProperty_ClientDataFormat, sizeof(output_format), &output_format); + if(noErr != error_result) + { + ExtAudioFileDispose(core_audio_file_container->extAudioFileRef); + AudioFileClose(*audio_file_id); + SDL_free(audio_file_id); + SDL_free(core_audio_file_container); + SNDDBG(("Core Audio: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) failed, reason: [%s].\n", CoreAudio_FourCCToString(error_result))); + BAIL_MACRO("Core Audio: Not valid audio data.", 0); + } + + + core_audio_file_container->outputFormat = (AudioStreamBasicDescription*)SDL_malloc(sizeof(AudioStreamBasicDescription)); + BAIL_IF_MACRO(core_audio_file_container->outputFormat == NULL, ERR_OUT_OF_MEMORY, 0); + + + + /* Copy the output format to the audio_description that was passed in so the + * info will be returned to the user. + */ + SDL_memcpy(core_audio_file_container->outputFormat, &output_format, sizeof(AudioStreamBasicDescription)); + + + + return 1; +} /* CoreAudio_open */ + + +static void CoreAudio_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; + + SDL_free(core_audio_file_container->outputFormat); + ExtAudioFileDispose(core_audio_file_container->extAudioFileRef); + AudioFileClose(*core_audio_file_container->audioFileID); + SDL_free(core_audio_file_container->audioFileID); + SDL_free(core_audio_file_container); +} /* CoreAudio_close */ + + +static Uint32 CoreAudio_read(Sound_Sample *sample) +{ + OSStatus error_result = noErr; + /* Documentation/example shows SInt64, but is problematic for big endian + * on 32-bit cast for ExtAudioFileRead() because it takes the upper + * bits which turn to 0. + */ + UInt32 buffer_size_in_frames = 0; + UInt32 buffer_size_in_frames_remaining = 0; + UInt32 total_frames_read = 0; + UInt32 data_buffer_size = 0; + UInt32 bytes_remaining = 0; + size_t total_bytes_read = 0; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; + UInt32 max_buffer_size = internal->buffer_size; + +// printf("internal->buffer_size=%d, internal->buffer=0x%x, sample->buffer_size=%d\n", internal->buffer_size, internal->buffer, sample->buffer_size); +// printf("internal->max_buffer_size=%d\n", max_buffer_size); + + /* Compute how many frames will fit into our max buffer size */ + /* Warning: If this is not evenly divisible, the buffer will not be completely filled which violates the SDL_sound assumption. */ + buffer_size_in_frames = max_buffer_size / core_audio_file_container->outputFormat->mBytesPerFrame; +// printf("buffer_size_in_frames=%ld, internal->buffer_size=%d, internal->buffer=0x%x outputFormat->mBytesPerFrame=%d, sample->buffer_size=%d\n", buffer_size_in_frames, internal->buffer_size, internal->buffer, core_audio_file_container->outputFormat->mBytesPerFrame, sample->buffer_size); + + +// void* temp_buffer = SDL_malloc(max_buffer_size); + + AudioBufferList audio_buffer_list; + audio_buffer_list.mNumberBuffers = 1; + audio_buffer_list.mBuffers[0].mDataByteSize = max_buffer_size; + audio_buffer_list.mBuffers[0].mNumberChannels = core_audio_file_container->outputFormat->mChannelsPerFrame; + audio_buffer_list.mBuffers[0].mData = internal->buffer; + + + bytes_remaining = max_buffer_size; + buffer_size_in_frames_remaining = buffer_size_in_frames; + + // oops. Due to the kAudioFormatFlagIsPacked bug, + // I was misled to believe that Core Audio + // was not always filling my entire requested buffer. + // So this while-loop might be unnecessary. + // However, I have not exhaustively tested all formats, + // so maybe it is possible this loop is useful. + // It might also handle the not-evenly disvisible case above. + while(buffer_size_in_frames_remaining > 0 && !(sample->flags & SOUND_SAMPLEFLAG_EOF)) + { + + data_buffer_size = (UInt32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame); +// printf("data_buffer_size=%d\n", data_buffer_size); + + buffer_size_in_frames = buffer_size_in_frames_remaining; + +// printf("reading buffer_size_in_frames=%"PRId64"\n", buffer_size_in_frames); + + + audio_buffer_list.mBuffers[0].mDataByteSize = bytes_remaining; + audio_buffer_list.mBuffers[0].mData = &(((UInt8*)internal->buffer)[total_bytes_read]); + + + /* Read the data into an AudioBufferList */ + error_result = ExtAudioFileRead(core_audio_file_container->extAudioFileRef, &buffer_size_in_frames, &audio_buffer_list); + if(error_result == noErr) + { + + + /* Success */ + + total_frames_read += buffer_size_in_frames; + buffer_size_in_frames_remaining = buffer_size_in_frames_remaining - buffer_size_in_frames; + +// printf("read buffer_size_in_frames=%"PRId64", buffer_size_in_frames_remaining=%"PRId64"\n", buffer_size_in_frames, buffer_size_in_frames_remaining); + + /* ExtAudioFileRead returns the number of frames actually read. Need to convert back to bytes. */ + data_buffer_size = (UInt32)(buffer_size_in_frames * core_audio_file_container->outputFormat->mBytesPerFrame); +// printf("data_buffer_size=%d\n", data_buffer_size); + + total_bytes_read += data_buffer_size; + bytes_remaining = bytes_remaining - data_buffer_size; + + /* Note: 0 == buffer_size_in_frames is a legitimate value meaning we are EOF. */ + if(0 == buffer_size_in_frames) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + } + + } + else + { + SNDDBG(("Core Audio: ExtAudioFileReadfailed, reason: [%s].\n", CoreAudio_FourCCToString(error_result))); + + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + break; + + } + } + + if( (!(sample->flags & SOUND_SAMPLEFLAG_EOF)) && (total_bytes_read < max_buffer_size)) + { + SNDDBG(("Core Audio: ExtAudioFileReadfailed SOUND_SAMPLEFLAG_EAGAIN, reason: [total_bytes_read < max_buffer_size], %d, %d.\n", total_bytes_read , max_buffer_size)); + + /* Don't know what to do here. */ + sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + } + return total_bytes_read; +} /* CoreAudio_read */ + + +static int CoreAudio_rewind(Sound_Sample *sample) +{ + OSStatus error_result = noErr; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; + + error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, 0); + if(error_result != noErr) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + } + return 1; +} /* CoreAudio_rewind */ + +/* Note: I found this tech note: + https://developer.apple.com/library/mac/#qa/qa2009/qa1678.html + I don't know if this applies to us. So far, I haven't noticed the problem, + so I haven't applied any of the techniques. + */ +static int CoreAudio_seek(Sound_Sample *sample, Uint32 ms) +{ + OSStatus error_result = noErr; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + CoreAudioFileContainer* core_audio_file_container = (CoreAudioFileContainer *) internal->decoder_private; + SInt64 frame_offset = 0; + AudioStreamBasicDescription actual_format; + UInt32 format_size; + + + /* I'm confused. The Apple documentation says this: + "Seek position is specified in the sample rate and frame count of the file’s audio data format + — not your application’s audio data format." + My interpretation is that I want to get the "actual format of the file and compute the frame offset. + But when I try that, seeking goes to the wrong place. + When I use outputFormat, things seem to work correctly. + I must be misinterpreting the documentation or doing something wrong. + */ +#if 0 /* not working */ + format_size = sizeof(AudioStreamBasicDescription); + error_result = AudioFileGetProperty( + *core_audio_file_container->audioFileID, + kAudioFilePropertyDataFormat, + &format_size, + &actual_format + ); + if(error_result != noErr) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + BAIL_MACRO("Core Audio: Could not GetProperty for kAudioFilePropertyDataFormat.", 0); + } /* if */ + + // packetIndex = (pos * sampleRate) / framesPerPacket + // frame_offset = (SInt64)((ms/1000.0 * actual_format.mSampleRate) / actual_format.mFramesPerPacket); +#else /* seems to work, but I'm confused */ + // packetIndex = (pos * sampleRate) / framesPerPacket + frame_offset = (SInt64)((ms/1000.0 * core_audio_file_container->outputFormat->mSampleRate) / core_audio_file_container->outputFormat->mFramesPerPacket); +#endif + + // computed against actual format and not the client format + error_result = ExtAudioFileSeek(core_audio_file_container->extAudioFileRef, frame_offset); + if(error_result != noErr) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + } + + return 1; +} /* CoreAudio_seek */ + + +static const char *extensions_coreaudio[] = +{ + "aif", + "aiff", + "aifc", + "wav", + "wave", + "mp3", + "mp4", + "m4a", + "aac", + "caf", + "Sd2f", + "Sd2", + "au", + "next", + "mp2", + "mp1", + "ac3", + "3gpp", + "3gp2", + "amrf", + "amr", + "ima4", + "ima", + NULL +}; +const Sound_DecoderFunctions __Sound_DecoderFunctions_CoreAudio = +{ + { + extensions_coreaudio, + "Decode audio through Core Audio through", + "Eric Wing ", + "https://playcontrol.net" + }, + + CoreAudio_init, /* init() method */ + CoreAudio_quit, /* quit() method */ + CoreAudio_open, /* open() method */ + CoreAudio_close, /* close() method */ + CoreAudio_read, /* read() method */ + CoreAudio_rewind, /* rewind() method */ + CoreAudio_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_COREAUDIO */ + +/* end of SDL_sound_coreaudio.c ... */ + diff --git a/SDL2_sound/SDL_sound_flac.c b/SDL2_sound/SDL_sound_flac.c new file mode 100644 index 00000000..136a11d6 --- /dev/null +++ b/SDL2_sound/SDL_sound_flac.c @@ -0,0 +1,182 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * FLAC decoder for SDL_sound. + * + * FLAC stands for Free Lossless Audio Codec. It's lossless and + * high-resolution, so it's become popular with tapers and audiophiles, at + * the cost of a much larger filesize than lossy things like Ogg, MP3, AAC, + * etc. More details can be found at https://xiph.org/flac/ ... + * + * This doesn't use libFLAC from xiph.org, but rather dr_flac, a public + * domain, single-header library, for decoding. It handles both FLAC and + * "Ogg FLAC" (FLAC codec wrapped in an Ogg container) files. + * + * dr_flac is here: https://github.com/mackron/dr_libs/ + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_FLAC + +#define DR_FLAC_IMPLEMENTATION +#define DR_FLAC_NO_STDIO 1 +#define DR_FLAC_NO_WIN32_IO 1 +#define DR_FLAC_NO_CRC 1 +#define DRFLAC_ASSERT(x) SDL_assert((x)) +#define DRFLAC_MALLOC(sz) SDL_malloc((sz)) +#define DRFLAC_REALLOC(p, sz) SDL_realloc((p), (sz)) +#define DRFLAC_FREE(p) SDL_free((p)) +#define DRFLAC_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz)) +#define DRFLAC_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz)) +#include "dr_flac.h" + +static size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + Uint8 *ptr = (Uint8 *) pBufferOut; + Sound_Sample *sample = (Sound_Sample *) pUserData; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rwops = internal->rw; + size_t retval = 0; + + /* !!! FIXME: dr_flac treats returning less than bytesToRead as EOF. So we can't EAGAIN. */ + while (retval < bytesToRead) + { + const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead); + if (rc == 0) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + break; + } /* if */ + else if (retval == -1) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + break; + } /* else if */ + else + { + retval += rc; + ptr += rc; + } /* else */ + } /* while */ + + return retval; +} /* flac_read */ + +static drflac_bool32 flac_seek(void* pUserData, int offset, drflac_seek_origin origin) +{ + const int whence = (origin == drflac_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR; + Sound_Sample *sample = (Sound_Sample *) pUserData; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRFLAC_TRUE : DRFLAC_FALSE; +} /* flac_seek */ + + +static int FLAC_init(void) +{ + return 1; /* always succeeds. */ +} /* FLAC_init */ + + +static void FLAC_quit(void) +{ + /* it's a no-op. */ +} /* FLAC_quit */ + + +static int FLAC_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drflac *dr = drflac_open(flac_read, flac_seek, sample); + + if (!dr) + { + BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_IO_ERROR, 0); + BAIL_MACRO("FLAC: Not a FLAC stream.", 0); + } /* if */ + + SNDDBG(("FLAC: Accepting data stream.\n")); + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + + sample->actual.channels = dr->channels; + sample->actual.rate = dr->sampleRate; + sample->actual.format = AUDIO_S32SYS; /* dr_flac only does Sint32. */ + + if (dr->totalSampleCount == 0) + internal->total_time = -1; + else + { + const Uint32 rate = (Uint32) dr->sampleRate; + const Uint64 frames = (Uint64) (dr->totalSampleCount / dr->channels); + internal->total_time = (frames / rate) * 1000; + internal->total_time += ((dr->totalSampleCount % dr->sampleRate) * 1000) / dr->sampleRate; + } /* else */ + + internal->decoder_private = dr; + + return 1; +} /* FLAC_open */ + +static void FLAC_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drflac *dr = (drflac *) internal->decoder_private; + drflac_close(dr); +} /* FLAC_close */ + +static Uint32 FLAC_read(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drflac *dr = (drflac *) internal->decoder_private; + const drflac_uint64 rc = drflac_read_s32(dr, internal->buffer_size / sizeof (drflac_int32), (drflac_int32 *) internal->buffer); + /* !!! FIXME: the flac_read callback sets ERROR and EOF flags, but this only tells you about i/o errors, not corruption. */ + return rc * sizeof (drflac_int32); +} /* FLAC_read */ + +static int FLAC_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drflac *dr = (drflac *) internal->decoder_private; + return (drflac_seek_to_sample(dr, 0) == DRFLAC_TRUE); +} /* FLAC_rewind */ + +static int FLAC_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drflac *dr = (drflac *) internal->decoder_private; + const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f; + const drflac_uint64 frame_offset = (drflac_uint64) (frames_per_ms * ((float) ms)); + const drflac_uint64 sampnum = frame_offset * sample->actual.channels; + return (drflac_seek_to_sample(dr, sampnum) == DRFLAC_TRUE); +} /* FLAC_seek */ + +static const char *extensions_flac[] = { "FLAC", "FLA", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC = +{ + { + extensions_flac, + "Free Lossless Audio Codec", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + FLAC_init, /* init() method */ + FLAC_quit, /* quit() method */ + FLAC_open, /* open() method */ + FLAC_close, /* close() method */ + FLAC_read, /* read() method */ + FLAC_rewind, /* rewind() method */ + FLAC_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_FLAC */ + +/* end of SDL_sound_flac.c ... */ + diff --git a/SDL2_sound/SDL_sound_internal.h b/SDL2_sound/SDL_sound_internal.h new file mode 100644 index 00000000..b9a9277f --- /dev/null +++ b/SDL2_sound/SDL_sound_internal.h @@ -0,0 +1,304 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * Internal function/structure declaration. Do NOT include in your + * application. + */ + +#ifndef _INCLUDE_SDL_SOUND_INTERNAL_H_ +#define _INCLUDE_SDL_SOUND_INTERNAL_H_ + +#ifndef __SDL_SOUND_INTERNAL__ +#error Do not include this header from your applications. +#endif + +#include +#include +#include +#include + +#include "SDL_sound.h" + +#if (defined(__GNUC__) && (__GNUC__ >= 4)) || defined(__clang__) +#define SOUND_HAVE_PRAGMA_VISIBILITY 1 +#endif + +#if SOUND_HAVE_PRAGMA_VISIBILITY +#pragma GCC visibility push(hidden) +#endif + +#if (defined DEBUG_CHATTER) +#define SNDDBG(x) SDL_LogDebug x +#else +#define SNDDBG(x) +#endif + +#ifndef SOUND_SUPPORTS_MP3 +#define SOUND_SUPPORTS_MP3 1 +#endif +#ifndef SOUND_SUPPORTS_MODPLUG +#define SOUND_SUPPORTS_MODPLUG 0 +#endif +#ifndef SOUND_SUPPORTS_WAV +#define SOUND_SUPPORTS_WAV 1 +#endif +#ifndef SOUND_SUPPORTS_AIFF +#define SOUND_SUPPORTS_AIFF 0 +#endif +#ifndef SOUND_SUPPORTS_AU +#define SOUND_SUPPORTS_AU 0 +#endif +#ifndef SOUND_SUPPORTS_VORBIS +#define SOUND_SUPPORTS_VORBIS 1 +#endif +#ifndef SOUND_SUPPORTS_VOC +#define SOUND_SUPPORTS_VOC 0 +#endif +#ifndef SOUND_SUPPORTS_RAW +#define SOUND_SUPPORTS_RAW 0 +#endif +#ifndef SOUND_SUPPORTS_SHN +#define SOUND_SUPPORTS_SHN 0 +#endif +#ifndef SOUND_SUPPORTS_FLAC +#define SOUND_SUPPORTS_FLAC 0 +#endif +#ifndef SOUND_SUPPORTS_COREAUDIO +#define SOUND_SUPPORTS_COREAUDIO 1 +#endif + +/* only build CoreAudio support if on an Apple platform. */ +#if SOUND_SUPPORTS_COREAUDIO && !defined(__APPLE__) +#undef SOUND_SUPPORTS_COREAUDIO +#define SOUND_SUPPORTS_COREAUDIO 0 +#endif + + +/* + * SDL itself only supports mono and stereo output, but hopefully we can + * raise this value someday...there's probably a lot of assumptions in + * SDL_sound that rely on it, though. + * !!! FIXME: SDL2 supports more channels. + */ +#define MAX_CHANNELS 2 + + +typedef struct __SOUND_DECODERFUNCTIONS__ +{ + /* This is a block of info about your decoder. See SDL_sound.h. */ + const Sound_DecoderInfo info; + + /* + * This is called during the Sound_Init() function. Use this to + * set up any global state that your decoder needs, such as + * initializing an external library, etc. + * + * Return non-zero if initialization is successful, zero if there's + * a fatal error. If this method fails, then this decoder is + * flagged as unavailable until SDL_sound() is shut down and + * reinitialized, in which case this method will be tried again. + * + * Note that the decoders quit() method won't be called if this + * method fails, so if you can't intialize, you'll have to clean + * up the half-initialized state in this method. + */ + int (*init)(void); + + /* + * This is called during the Sound_Quit() function. Use this to + * clean up any global state that your decoder has used during its + * lifespan. + */ + void (*quit)(void); + + /* + * Returns non-zero if (sample) has a valid fileformat that this + * driver can handle. Zero if this driver can NOT handle the data. + * + * Extension, which may be NULL, is just a hint as to the form of + * data that is being passed in. Most decoders should determine if + * they can handle the data by the data itself, but others, like + * the raw data handler, need this hint to know if they should + * accept the data in the first place. + * + * (sample)'s (opaque) field should be cast to a Sound_SampleInternal + * pointer: + * + * Sound_SampleInternal *internal; + * internal = (Sound_SampleInternal *) sample->opaque; + * + * Certain fields of sample will be filled in for the decoder before + * this call, and others should be filled in by the decoder. Some + * fields are offlimits, and should NOT be modified. The list: + * + * in Sound_SampleInternal section: + * Sound_Sample *next; (offlimits) + * Sound_Sample *prev; (offlimits) + * SDL_RWops *rw; (can use, but do NOT close it) + * const Sound_DecoderFunctions *funcs; (that's this structure) + * SDL_AudioCVT sdlcvt; (offlimits) + * void *buffer; (offlimits until read() method) + * Uint32 buffer_size; (offlimits until read() method) + * void *decoder_private; (read and write access) + * + * in rest of Sound_Sample: + * void *opaque; (this was internal section, above) + * const Sound_DecoderInfo *decoder; (read only) + * Sound_AudioInfo desired; (read only, usually not needed here) + * Sound_AudioInfo actual; (please fill this in) + * void *buffer; (offlimits) + * Uint32 buffer_size; (offlimits) + * Sound_SampleFlags flags; (set appropriately) + */ + int (*open)(Sound_Sample *sample, const char *ext); + + /* + * Clean up. SDL_sound is done with this sample, so the decoder should + * clean up any resources it allocated. Anything that wasn't + * explicitly allocated by the decoder should be LEFT ALONE, since + * the higher-level SDL_sound layer will clean up its own mess. + */ + void (*close)(Sound_Sample *sample); + + /* + * Get more data from (sample). The decoder should get a pointer to + * the internal structure... + * + * Sound_SampleInternal *internal; + * internal = (Sound_SampleInternal *) sample->opaque; + * + * ...and then start decoding. Fill in up to internal->buffer_size + * bytes of decoded sound in the space pointed to by + * internal->buffer. The encoded data is read in from internal->rw. + * Data should be decoded in the format specified during the + * decoder's open() method in the sample->actual field. The + * conversion to the desired format is done at a higher level. + * + * The return value is the number of bytes decoded into + * internal->buffer, which can be no more than internal->buffer_size, + * but can be less. If it is less, you should set a state flag: + * + * If there's just no more data (end of file, etc), then do: + * sample->flags |= SOUND_SAMPLEFLAG_EOF; + * + * If there's an unrecoverable error, then do: + * __Sound_SetError(ERR_EXPLAIN_WHAT_WENT_WRONG); + * sample->flags |= SOUND_SAMPLEFLAG_ERROR; + * + * If there's more data, but you'd have to block for considerable + * amounts of time to get at it, or there's a recoverable error, + * then do: + * __Sound_SetError(ERR_EXPLAIN_WHAT_WENT_WRONG); + * sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + * + * SDL_sound will not call your read() method for any samples with + * SOUND_SAMPLEFLAG_EOF or SOUND_SAMPLEFLAG_ERROR set. The + * SOUND_SAMPLEFLAG_EAGAIN flag is reset before each call to this + * method. + */ + Uint32 (*read)(Sound_Sample *sample); + + /* + * Reset the decoding to the beginning of the stream. Nonzero on + * success, zero on failure. + * + * The purpose of this method is to allow for higher efficiency than + * an application could get by just recreating the sample externally; + * not only do they not have to reopen the RWops, reallocate buffers, + * and potentially pass the data through several rejecting decoders, + * but certain decoders will not have to recreate their existing + * state (search for metadata, etc) since they already know they + * have a valid audio stream with a given set of characteristics. + * + * The decoder is responsible for calling seek() on the associated + * SDL_RWops. A failing call to seek() should be the ONLY reason that + * this method should ever fail! + */ + int (*rewind)(Sound_Sample *sample); + + /* + * Reposition the decoding to an arbitrary point. Nonzero on + * success, zero on failure. + * + * The purpose of this method is to allow for higher efficiency than + * an application could get by just rewinding the sample and + * decoding to a given point. + * + * The decoder is responsible for calling seek() on the associated + * SDL_RWops. + * + * If there is an error, try to recover so that the next read will + * continue as if nothing happened. + */ + int (*seek)(Sound_Sample *sample, Uint32 ms); +} Sound_DecoderFunctions; + + +typedef void (*MixFunc)(float *dst, void *src, Uint32 frames, float *gains); + +typedef struct __SOUND_SAMPLEINTERNAL__ +{ + Sound_Sample *next; + Sound_Sample *prev; + SDL_RWops *rw; + const Sound_DecoderFunctions *funcs; + SDL_AudioCVT sdlcvt; + void *buffer; + Uint32 buffer_size; + void *decoder_private; + Sint32 total_time; + Uint32 mix_position; + MixFunc mix; +} Sound_SampleInternal; + + +/* error messages... */ +#define ERR_IS_INITIALIZED "Already initialized" +#define ERR_NOT_INITIALIZED "Not initialized" +#define ERR_INVALID_ARGUMENT "Invalid argument" +#define ERR_OUT_OF_MEMORY "Out of memory" +#define ERR_NOT_SUPPORTED "Operation not supported" +#define ERR_UNSUPPORTED_FORMAT "Sound format unsupported" +#define ERR_NOT_A_HANDLE "Not a file handle" +#define ERR_NO_SUCH_FILE "No such file" +#define ERR_PAST_EOF "Past end of file" +#define ERR_IO_ERROR "I/O error" +#define ERR_COMPRESSION "(De)compression error" +#define ERR_PREV_ERROR "Previous decoding already caused an error" +#define ERR_PREV_EOF "Previous decoding already triggered EOF" +#define ERR_CANNOT_SEEK "Sample is not seekable" + +/* + * Call this to set the message returned by Sound_GetError(). + * Please only use the ERR_* constants above, or add new constants to the + * above group, but I want these all in one place. + * + * Calling this with a NULL argument is a safe no-op. + */ +void __Sound_SetError(const char *err); + +/* + * Call this to convert milliseconds to an actual byte position, based on + * audio data characteristics. + */ +Uint32 __Sound_convertMsToBytePos(Sound_AudioInfo *info, Uint32 ms); + + +/* These get used all over for lessening code clutter. */ +#define BAIL_MACRO(e, r) { __Sound_SetError(e); return r; } +#define BAIL_IF_MACRO(c, e, r) if (c) { __Sound_SetError(e); return r; } + +#ifdef __cplusplus +extern "C" { +#endif + +#endif /* defined _INCLUDE_SDL_SOUND_INTERNAL_H_ */ + +/* end of SDL_sound_internal.h ... */ + diff --git a/SDL2_sound/SDL_sound_modplug.c b/SDL2_sound/SDL_sound_modplug.c new file mode 100644 index 00000000..71da8052 --- /dev/null +++ b/SDL2_sound/SDL_sound_modplug.c @@ -0,0 +1,228 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Torbjörn Andersson. + */ + +/* + * Module player for SDL_sound. This driver handles anything that ModPlug does. + * + * ModPlug can be found at https://sourceforge.net/projects/modplug-xmms + * + * An unofficial version of modplug with all C++ dependencies removed is also + * available: http://freecraft.net/snapshots/ + * (Look for something like "libmodplug-johns-*.tar.gz") + * (update: this domain is gone. --ryan.) + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_MODPLUG + +#include "libmodplug/modplug.h" + +static const char *extensions_modplug[] = +{ + /* The XMMS plugin is apparently able to load compressed modules as + * well, but libmodplug does not handle this. + */ + "669", /* Composer 669 / UNIS 669 module */ + "AMF", /* ASYLUM Music Format / Advanced Music Format(DSM) */ + "AMS", /* AMS module */ + "DBM", /* DigiBooster Pro Module */ + "DMF", /* DMF DELUSION DIGITAL MUSIC FILEFORMAT (X-Tracker) */ + "DSM", /* DSIK Internal Format module */ + "FAR", /* Farandole module */ + "IT", /* Impulse Tracker IT file */ + "MDL", /* DigiTracker module */ + "MED", /* OctaMed MED file */ + "MOD", /* ProTracker / NoiseTracker MOD/NST file */ + "MT2", /* MadTracker 2.0 */ + "MTM", /* MTM file */ + "OKT", /* Oktalyzer module */ + "PTM", /* PTM PolyTracker module */ + "PSM", /* PSM module */ + "S3M", /* ScreamTracker file */ + "STM", /* ST 2.xx */ + "ULT", + "UMX", + "XM", /* FastTracker II */ + "ABC", + "MID", + "MIDI", + NULL +}; + + + +static int MODPLUG_init(void) +{ + return ModPlug_Init(); /* success. */ +} /* MODPLUG_init */ + + +static void MODPLUG_quit(void) +{ + /* it's a no-op. */ +} /* MODPLUG_quit */ + + +/* + * Most MOD files I've seen have tended to be a few hundred KB, even if some + * of them were much smaller than that. + */ +#define CHUNK_SIZE 65536 + +static int MODPLUG_open(Sound_Sample *sample, const char *ext) +{ + ModPlug_Settings settings; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + ModPlugFile *module; + Uint8 *data; + size_t size; + Uint32 retval; + int has_extension = 0; + int i; + + /* + * Apparently ModPlug's loaders are too forgiving. They gladly accept + * streams that they shouldn't. For now, rely on file extension instead. + */ + for (i = 0; extensions_modplug[i] != NULL; i++) + { + if (SDL_strcasecmp(ext, extensions_modplug[i]) == 0) + { + has_extension = 1; + break; + } /* if */ + } /* for */ + + if (!has_extension) + { + SNDDBG(("MODPLUG: Unrecognized file type: %s\n", ext)); + BAIL_MACRO("MODPLUG: Not a module file.", 0); + } /* if */ + + /* ModPlug needs the entire stream in one big chunk. I don't like it, + but I don't think there's any way around it. !!! FIXME: rework modplug? */ + data = (Uint8 *) SDL_malloc(CHUNK_SIZE); + BAIL_IF_MACRO(data == NULL, ERR_OUT_OF_MEMORY, 0); + size = 0; + + do + { + retval = SDL_RWread(internal->rw, &data[size], 1, CHUNK_SIZE); + size += retval; + if (retval == CHUNK_SIZE) + { + data = (Uint8 *) SDL_realloc(data, size + CHUNK_SIZE); + BAIL_IF_MACRO(data == NULL, ERR_OUT_OF_MEMORY, 0); + } /* if */ + } while (retval > 0); + + SDL_memcpy(&sample->actual, &sample->desired, sizeof (Sound_AudioInfo)); + if (sample->actual.rate == 0) sample->actual.rate = 44100; + if (sample->actual.channels == 0) sample->actual.channels = 2; + if (sample->actual.format == 0) sample->actual.format = AUDIO_S16SYS; + + settings.mChannels=sample->actual.channels; + settings.mFrequency=sample->actual.rate; + settings.mBits = sample->actual.format & 0xFF; + + /* The settings will require some experimenting. I've borrowed some + of them from the XMMS ModPlug plugin. */ + settings.mFlags = MODPLUG_ENABLE_OVERSAMPLING; + settings.mFlags |= MODPLUG_ENABLE_NOISE_REDUCTION | + MODPLUG_ENABLE_MEGABASS | + MODPLUG_ENABLE_SURROUND; + + settings.mReverbDepth = 30; + settings.mReverbDelay = 100; + settings.mBassAmount = 40; + settings.mBassRange = 30; + settings.mSurroundDepth = 20; + settings.mSurroundDelay = 20; + settings.mChannels = 2; + settings.mBits = 16; + settings.mFrequency = 44100; + settings.mResamplingMode = MODPLUG_RESAMPLE_FIR; + settings.mLoopCount = 0; + + /* The buffer may be a bit too large, but that doesn't matter. I think + it's safe to free it as soon as ModPlug_Load() is finished anyway. */ + module = ModPlug_Load((void *) data, size, &settings); + SDL_free(data); + BAIL_IF_MACRO(module == NULL, "MODPLUG: Not a module file.", 0); + + internal->total_time = ModPlug_GetLength(module); + internal->decoder_private = (void *) module; + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + + SNDDBG(("MODPLUG: Accepting data stream\n")); + return 1; /* we'll handle this data. */ +} /* MODPLUG_open */ + + +static void MODPLUG_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + ModPlugFile *module = (ModPlugFile *) internal->decoder_private; + ModPlug_Unload(module); +} /* MODPLUG_close */ + + +static Uint32 MODPLUG_read(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + ModPlugFile *module = (ModPlugFile *) internal->decoder_private; + int retval; + retval = ModPlug_Read(module, internal->buffer, internal->buffer_size); + if (retval == 0) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + return retval; +} /* MODPLUG_read */ + + +static int MODPLUG_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + ModPlugFile *module = (ModPlugFile *) internal->decoder_private; + ModPlug_Seek(module, 0); + return 1; +} /* MODPLUG_rewind */ + + +static int MODPLUG_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + ModPlugFile *module = (ModPlugFile *) internal->decoder_private; + ModPlug_Seek(module, ms); + return 1; +} /* MODPLUG_seek */ + + +const Sound_DecoderFunctions __Sound_DecoderFunctions_MODPLUG = +{ + { + extensions_modplug, + "Play modules through ModPlug", + "Torbjörn Andersson ", + "http://modplug-xmms.sourceforge.net/" + }, + + MODPLUG_init, /* init() method */ + MODPLUG_quit, /* quit() method */ + MODPLUG_open, /* open() method */ + MODPLUG_close, /* close() method */ + MODPLUG_read, /* read() method */ + MODPLUG_rewind, /* rewind() method */ + MODPLUG_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_MODPLUG */ + +/* end of SDL_sound_modplug.c ... */ + diff --git a/SDL2_sound/SDL_sound_mp3.c b/SDL2_sound/SDL_sound_mp3.c new file mode 100644 index 00000000..6aa5666d --- /dev/null +++ b/SDL2_sound/SDL_sound_mp3.c @@ -0,0 +1,184 @@ +/** + * SDL_sound; An abstract sound format decoding API. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * MP3 decoder for SDL_sound. + * + * !!! FIXME: write something here. + * + * dr_mp3 is here: https://github.com/mackron/dr_libs/ + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_MP3 + +#define DR_MP3_IMPLEMENTATION +#define DR_MP3_NO_STDIO 1 +#define DRMP3_ASSERT(x) SDL_assert((x)) +#define DRMP3_MALLOC(sz) SDL_malloc((sz)) +#define DRMP3_REALLOC(p, sz) SDL_realloc((p), (sz)) +#define DRMP3_FREE(p) SDL_free((p)) +#define DRMP3_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz)) +#define DRMP3_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz)) + +#if !defined(__clang_analyzer__) +#ifdef memset +#undef memset +#endif +#ifdef memcpy +#undef memcpy +#endif +#ifdef memmove +#undef memmove +#endif +#define memset SDL_memset +#define memcpy SDL_memcpy +#define memmove SDL_memmove +#endif + +#include "dr_mp3.h" + +static size_t mp3_read(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + Uint8 *ptr = (Uint8 *) pBufferOut; + Sound_Sample *sample = (Sound_Sample *) pUserData; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rwops = internal->rw; + size_t retval = 0; + + /* !!! FIXME: dr_mp3 treats returning less than bytesToRead as EOF. So we can't EAGAIN. */ + while (retval < bytesToRead) + { + const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead); + if (rc == 0) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + break; + } /* if */ + else if (retval == -1) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + break; + } /* else if */ + else + { + retval += rc; + ptr += rc; + } /* else */ + } /* while */ + + return retval; +} /* mp3_read */ + +static drmp3_bool32 mp3_seek(void* pUserData, int offset, drmp3_seek_origin origin) +{ + const int whence = (origin == drmp3_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR; + Sound_Sample *sample = (Sound_Sample *) pUserData; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRMP3_TRUE : DRMP3_FALSE; +} /* mp3_seek */ + + +static int MP3_init(void) +{ + return 1; /* always succeeds. */ +} /* MP3_init */ + + +static void MP3_quit(void) +{ + /* it's a no-op. */ +} /* MP3_quit */ + +static int MP3_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drmp3 *dr = (drmp3 *) SDL_calloc(1, sizeof (drmp3)); + + BAIL_IF_MACRO(!dr, ERR_OUT_OF_MEMORY, 0); + if (drmp3_init(dr, mp3_read, mp3_seek, sample, NULL) != DRMP3_TRUE) + { + SDL_free(dr); + BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_IO_ERROR, 0); + BAIL_MACRO("MP3: Not an MPEG-1 layer 1-3 stream.", 0); + } /* if */ + + SNDDBG(("MP3: Accepting data stream.\n")); + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + + sample->actual.channels = dr->channels; + sample->actual.rate = dr->sampleRate; + sample->actual.format = AUDIO_F32SYS; /* dr_mp3 only does float. */ + + internal->total_time = -1; /* !!! FIXME? */ + internal->decoder_private = dr; + + return 1; +} /* MP3_open */ + +static void MP3_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drmp3 *dr = (drmp3 *) internal->decoder_private; + drmp3_uninit(dr); + SDL_free(dr); +} /* MP3_close */ + +static Uint32 MP3_read(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + const int channels = (int) sample->actual.channels; + drmp3 *dr = (drmp3 *) internal->decoder_private; + const drmp3_uint64 frames_to_read = (internal->buffer_size / channels) / sizeof (float); + const drmp3_uint64 rc = drmp3_read_f32(dr, frames_to_read, (float *) internal->buffer); + /* !!! FIXME: the mp3_read callback sets ERROR and EOF flags, but this only tells you about i/o errors, not corruption. */ + return rc * channels * sizeof (float); +} /* MP3_read */ + +static int MP3_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drmp3 *dr = (drmp3 *) internal->decoder_private; + return (drmp3_seek_to_frame(dr, 0) == DRMP3_TRUE); +} /* MP3_rewind */ + +static int MP3_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + drmp3 *dr = (drmp3 *) internal->decoder_private; + const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f; + const drmp3_uint64 frame_offset = (drmp3_uint64) (frames_per_ms * ((float) ms)); + return (drmp3_seek_to_frame(dr, frame_offset) == DRMP3_TRUE); +} /* MP3_seek */ + +/* dr_mp3 will play layer 1 and 2 files, too */ +static const char *extensions_mp3[] = { "MP3", "MP2", "MP1", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3 = +{ + { + extensions_mp3, + "MPEG-1 Audio Layer I-III", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + MP3_init, /* init() method */ + MP3_quit, /* quit() method */ + MP3_open, /* open() method */ + MP3_close, /* close() method */ + MP3_read, /* read() method */ + MP3_rewind, /* rewind() method */ + MP3_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_MP3 */ + +/* end of SDL_sound_mp3.c ... */ + diff --git a/SDL2_sound/SDL_sound_raw.c b/SDL2_sound/SDL_sound_raw.c new file mode 100644 index 00000000..48616f10 --- /dev/null +++ b/SDL2_sound/SDL_sound_raw.c @@ -0,0 +1,164 @@ +/** + * SDL_sound; A sound processing toolkit. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * RAW decoder for SDL_sound. This is as simple as it gets. + * + * This driver handles raw audio data. You must, regardless of where the + * data is actually coming from, specify the string "RAW" in the extension + * parameter of Sound_NewSample() (or, alternately, open a file with the + * extension ".raw" in Sound_NewSampleFromFile()). The string is checked + * case-insensitive. We need this check, because raw data, being raw, has + * no headers or magic number we can use to determine if we should handle a + * given file, so we needed some way to have this "decoder" discriminate. + * + * When calling Sound_NewSample*(), you must also specify a "desired" + * audio format. The "actual" format will always match what you specify, so + * there will be no conversion overhead, but these routines need to know how + * to treat the bits, since it's all random garbage otherwise. + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_RAW + +static int RAW_init(void) +{ + return 1; /* always succeeds. */ +} /* RAW_init */ + + +static void RAW_quit(void) +{ + /* it's a no-op. */ +} /* RAW_quit */ + + +static int RAW_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = sample->opaque; + SDL_RWops *rw = internal->rw; + Uint32 pos, sample_rate; + + /* + * We check this explicitly, since we have no other way to + * determine whether we should handle this data or not. + */ + if (SDL_strcasecmp(ext, "RAW") != 0) + BAIL_MACRO("RAW: extension isn't explicitly \"RAW\".", 0); + + /* + * You must also specify a desired format, so we know how to + * treat the bits that are otherwise binary garbage. + */ + if ( (sample->desired.channels < 1) || + (sample->desired.channels > 2) || + (sample->desired.rate == 0) || + (sample->desired.format == 0) ) + { + BAIL_MACRO("RAW: invalid desired format.", 0); + } /* if */ + + SNDDBG(("RAW: Accepting data stream.\n")); + + /* + * We never convert raw samples; what you ask for is what you get. + */ + SDL_memcpy(&sample->actual, &sample->desired, sizeof (Sound_AudioInfo)); + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + + if ( (pos = SDL_RWseek(internal->rw, 0, SEEK_END) ) <= 0) { + BAIL_MACRO("RAW: cannot seek the end of the file \"RAW\".", 0); + } + if ( SDL_RWseek(internal->rw, 0, SEEK_SET) ) { + BAIL_MACRO("RAW: cannot reset file \"RAW\".", 0); + } + + sample_rate = (sample->actual.rate * sample->actual.channels + * ( (sample->actual.format & 0x0018) >> 3) ); + internal->total_time = ( pos ) / sample_rate * 1000; + internal->total_time += (pos % sample_rate) * 1000 / sample_rate; + + return 1; /* we'll handle this data. */ +} /* RAW_open */ + + +static void RAW_close(Sound_Sample *sample) +{ + /* we don't allocate anything that we need to free. That's easy, eh? */ +} /* RAW_close */ + + +static Uint32 RAW_read(Sound_Sample *sample) +{ + Uint32 retval; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + + /* + * We don't actually do any decoding, so we read the raw data + * directly into the internal buffer... + */ + retval = SDL_RWread(internal->rw, internal->buffer, + 1, internal->buffer_size); + + /* Make sure the read went smoothly... */ + if (retval == 0) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + + else if (retval == -1) + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + + /* (next call this EAGAIN may turn into an EOF or error.) */ + else if (retval < internal->buffer_size) + sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + + return retval; +} /* RAW_read */ + + +static int RAW_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0); + return 1; +} /* RAW_rewind */ + + +static int RAW_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + int pos = (int) __Sound_convertMsToBytePos(&sample->actual, ms); + int err = (SDL_RWseek(internal->rw, pos, SEEK_SET) != pos); + BAIL_IF_MACRO(err, ERR_IO_ERROR, 0); + return 1; +} /* RAW_seek */ + +static const char *extensions_raw[] = { "RAW", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_RAW = +{ + { + extensions_raw, + "Raw audio", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + RAW_init, /* init() method */ + RAW_quit, /* quit() method */ + RAW_open, /* open() method */ + RAW_close, /* close() method */ + RAW_read, /* read() method */ + RAW_rewind, /* rewind() method */ + RAW_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_RAW */ + +/* end of SDL_sound_raw.c ... */ + diff --git a/SDL2_sound/SDL_sound_shn.c b/SDL2_sound/SDL_sound_shn.c new file mode 100644 index 00000000..f03010e6 --- /dev/null +++ b/SDL2_sound/SDL_sound_shn.c @@ -0,0 +1,1309 @@ +/** + * SDL_sound; A sound processing toolkit. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * Shorten decoder for SDL_sound. + * + * This driver handles Shorten-compressed waveforms. Despite the fact that + * SHNs tend to be much bigger than MP3s, they are still the de facto + * standard in online music trading communities. If an MP3 crunches the + * waveform to 10-20 percent of its original size, SHNs only go to about + * 50-60%. Why do the Phish fans of the world use this format then? Rabid + * music traders appreciate the sound quality; SHNs, unlike MP3s, do not + * throw away any part of the waveform. Yes, there are people that notice + * this, and further more, they demand it...and if they can't get a good + * transfer of those larger files over the 'net, they haven't underestimated + * the bandwidth of CDs travelling the world through the postal system. + * + * Shorten homepage: http://www.softsound.com/Shorten.html + * + * !!! FIXME: softsound.com is gone, I think. + * + * The Shorten format was gleaned from the shorten codebase, by Tony + * Robinson and SoftSound Limited. + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_SHN + +#define SHN_BUFSIZ 512 + +typedef struct +{ + Sint32 version; + Sint32 datatype; + Sint32 nchan; + Sint32 blocksize; + Sint32 maxnlpc; + Sint32 nmean; + Sint32 nwrap; + Sint32 **buffer; + Sint32 **offset; + Sint32 *qlpc; + Sint32 lpcqoffset; + Sint32 bitshift; + int nbitget; + int nbyteget; + Uint8 *getbuf; + Uint8 *getbufp; + Uint32 gbuffer; + Uint8 *backBuffer; + Uint32 backBufferSize; + Uint32 backBufLeft; + Uint32 start_pos; +} shn_t; + + +static const Uint32 mask_table[] = +{ + 0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, + 0x0000003F, 0x0000007F, 0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, + 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, 0x0001FFFF, + 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, + 0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, + 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF +}; + + +static const Uint8 ulaw_outward[13][256] = { +{127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128}, +{112,114,116,118,120,122,124,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,113,115,117,119,121,123,125,255,253,251,249,247,245,243,241,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,252,250,248,246,244,242,240}, +{96,98,100,102,104,106,108,110,112,113,114,116,117,118,120,121,122,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,97,99,101,103,105,107,109,111,115,119,123,255,251,247,243,239,237,235,233,231,229,227,225,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,250,249,248,246,245,244,242,241,240,238,236,234,232,230,228,226,224}, +{80,82,84,86,88,90,92,94,96,97,98,100,101,102,104,105,106,108,109,110,112,113,114,115,116,117,118,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,81,83,85,87,89,91,93,95,99,103,107,111,119,255,247,239,235,231,227,223,221,219,217,215,213,211,209,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,246,245,244,243,242,241,240,238,237,236,234,233,232,230,229,228,226,225,224,222,220,218,216,214,212,210,208}, +{64,66,68,70,72,74,76,78,80,81,82,84,85,86,88,89,90,92,93,94,96,97,98,99,100,101,102,104,105,106,107,108,109,110,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,65,67,69,71,73,75,77,79,83,87,91,95,103,111,255,239,231,223,219,215,211,207,205,203,201,199,197,195,193,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,238,237,236,235,234,233,232,230,229,228,227,226,225,224,222,221,220,218,217,216,214,213,212,210,209,208,206,204,202,200,198,196,194,192}, +{49,51,53,55,57,59,61,63,64,66,67,68,70,71,72,74,75,76,78,79,80,81,82,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,50,52,54,56,58,60,62,65,69,73,77,83,91,103,255,231,219,211,205,201,197,193,190,188,186,184,182,180,178,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,230,229,228,227,226,225,224,223,222,221,220,218,217,216,215,214,213,212,210,209,208,207,206,204,203,202,200,199,198,196,195,194,192,191,189,187,185,183,181,179,177}, +{32,34,36,38,40,42,44,46,48,49,51,52,53,55,56,57,59,60,61,63,64,65,66,67,68,70,71,72,73,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,90,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,35,37,39,41,43,45,47,50,54,58,62,69,77,91,255,219,205,197,190,186,182,178,175,173,171,169,167,165,163,161,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,218,217,216,215,214,213,212,211,210,209,208,207,206,204,203,202,201,200,199,198,196,195,194,193,192,191,189,188,187,185,184,183,181,180,179,177,176,174,172,170,168,166,164,162,160}, +{16,18,20,22,24,26,28,30,32,33,34,36,37,38,40,41,42,44,45,46,48,49,50,51,52,53,55,56,57,58,59,60,61,63,64,65,66,67,68,69,70,71,72,73,74,75,76,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,17,19,21,23,25,27,29,31,35,39,43,47,54,62,77,255,205,190,182,175,171,167,163,159,157,155,153,151,149,147,145,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,204,203,202,201,200,199,198,197,196,195,194,193,192,191,189,188,187,186,185,184,183,181,180,179,178,177,176,174,173,172,170,169,168,166,165,164,162,161,160,158,156,154,152,150,148,146,144}, +{2,4,6,8,10,12,14,16,17,18,20,21,22,24,25,26,28,29,30,32,33,34,35,36,37,38,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,57,58,59,60,61,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,1,3,5,7,9,11,13,15,19,23,27,31,39,47,62,255,190,175,167,159,155,151,147,143,141,139,137,135,133,131,129,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,189,188,187,186,185,184,183,182,181,180,179,178,177,176,174,173,172,171,170,169,168,166,165,164,163,162,161,160,158,157,156,154,153,152,150,149,148,146,145,144,142,140,138,136,134,132,130,128}, +{1,2,4,5,6,8,9,10,12,13,14,16,17,18,19,20,21,22,24,25,26,27,28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,3,7,11,15,23,31,47,255,175,159,151,143,139,135,131,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,158,157,156,155,154,153,152,150,149,148,147,146,145,144,142,141,140,138,137,136,134,133,132,130,129,128}, +{1,2,3,4,5,6,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,7,15,31,255,159,143,135,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,142,141,140,139,138,137,136,134,133,132,131,130,129,128}, +{1,2,3,4,5,6,7,8,9,10,11,12,13,14,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,15,255,143,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128}, +{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,0,255,254,253,252,251,250,249,248,247,246,245,244,243,242,241,240,239,238,237,236,235,234,233,232,231,230,229,228,227,226,225,224,223,222,221,220,219,218,217,216,215,214,213,212,211,210,209,208,207,206,205,204,203,202,201,200,199,198,197,196,195,194,193,192,191,190,189,188,187,186,185,184,183,182,181,180,179,178,177,176,175,174,173,172,171,170,169,168,167,166,165,164,163,162,161,160,159,158,157,156,155,154,153,152,151,150,149,148,147,146,145,144,143,142,141,140,139,138,137,136,135,134,133,132,131,130,129,128} +}; + + +#ifndef MIN_MACRO +#define MIN_MACRO(a,b) (((a)<(b))?(a):(b)) +#endif + +#ifndef MAX_MACRO +#define MAX_MACRO(a,b) (((a)>(b))?(a):(b)) +#endif + +#define POSITIVE_ULAW_ZERO 0xff +#define NEGATIVE_ULAW_ZERO 0x7f + +#define CAPMAXSCHAR(x) ((x > 127) ? 127 : x) +#define CAPMAXUCHAR(x) ((x > 255) ? 255 : x) +#define CAPMAXSHORT(x) ((x > 32767) ? 32767 : x) +#define CAPMAXUSHORT(x) ((x > 65535) ? 65535 : x) + +#define UNDEFINED_UINT -1 +#define DEFAULT_BLOCK_SIZE 256 +#define DEFAULT_V0NMEAN 0 +#define DEFAULT_V2NMEAN 4 +#define DEFAULT_MAXNLPC 0 +#define DEFAULT_NCHAN 1 +#define DEFAULT_NSKIP 0 +#define DEFAULT_NDISCARD 0 +#define NBITPERLONG 32 +#define DEFAULT_MINSNR 256 +#define DEFAULT_QUANTERROR 0 +#define MINBITRATE 2.5 + +#define MEAN_VERSION0 0 +#define MEAN_VERSION2 4 + +#define SHN_FN_DIFF0 0 +#define SHN_FN_DIFF1 1 +#define SHN_FN_DIFF2 2 +#define SHN_FN_DIFF3 3 +#define SHN_FN_QUIT 4 +#define SHN_FN_BLOCKSIZE 5 +#define SHN_FN_BITSHIFT 6 +#define SHN_FN_QLPC 7 +#define SHN_FN_ZERO 8 +#define SHN_FN_VERBATIM 9 + +#define SHN_TYPE_AU1 0 +#define SHN_TYPE_S8 1 +#define SHN_TYPE_U8 2 +#define SHN_TYPE_S16HL 3 +#define SHN_TYPE_U16HL 4 +#define SHN_TYPE_S16LH 5 +#define SHN_TYPE_U16LH 6 +#define SHN_TYPE_ULAW 7 +#define SHN_TYPE_AU2 8 +#define SHN_TYPE_AU3 9 +#define SHN_TYPE_ALAW 10 +#define SHN_TYPE_RIFF_WAVE 11 +#define SHN_TYPE_EOF 12 +#define SHN_TYPE_GENERIC_ULAW 128 +#define SHN_TYPE_GENERIC_ALAW 129 + +#define SHN_FNSIZE 2 +#define SHN_CHANNELSIZE 0 +#define SHN_TYPESIZE 4 +#define SHN_ULONGSIZE 2 +#define SHN_NSKIPSIZE 1 +#define SHN_LPCQSIZE 2 +#define SHN_LPCQUANT 5 +#define SHN_XBYTESIZE 7 +#define SHN_VERBATIM_CKSIZE_SIZE 5 +#define SHN_VERBATIM_BYTE_SIZE 8 +#define SHN_ENERGYSIZE 3 +#define SHN_BITSHIFTSIZE 2 + +#define SHN_LPCQOFFSET_VER2 (1 << SHN_LPCQUANT) + + +#define SHN_MAGIC 0x676B6A61 /* looks like "ajkg" as chars. */ + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + + +static int word_get(shn_t *shn, SDL_RWops *rw, Uint32 *word) +{ + if (shn->nbyteget < 4) + { + shn->nbyteget += SDL_RWread(rw, shn->getbuf, 1, SHN_BUFSIZ); + BAIL_IF_MACRO(shn->nbyteget < 4, NULL, 0); + shn->getbufp = shn->getbuf; + } /* if */ + + if (word != NULL) + { + *word = (((Sint32) shn->getbufp[0]) << 24) | + (((Sint32) shn->getbufp[1]) << 16) | + (((Sint32) shn->getbufp[2]) << 8) | + (((Sint32) shn->getbufp[3]) ); + } /* if */ + + shn->getbufp += 4; + shn->nbyteget -= 4; + + return 1; +} /* word_get */ + + +static int uvar_get(int nbin, shn_t *shn, SDL_RWops *rw, Sint32 *word) +{ + Sint32 result; + + if (shn->nbitget == 0) + { + BAIL_IF_MACRO(!word_get(shn, rw, &shn->gbuffer), NULL, 0); + shn->nbitget = 32; + } /* if */ + + for (result = 0; !(shn->gbuffer & (1L << --shn->nbitget)); result++) + { + if (shn->nbitget == 0) + { + BAIL_IF_MACRO(!word_get(shn, rw, &shn->gbuffer), NULL, 0); + shn->nbitget = 32; + } /* if */ + } /* for */ + + while (nbin != 0) + { + if (shn->nbitget >= nbin) + { + result = ( (result << nbin) | + ((shn->gbuffer >> (shn->nbitget - nbin)) & + mask_table[nbin]) ); + shn->nbitget -= nbin; + break; + } /* if */ + else + { + result = (result << shn->nbitget) | + (shn->gbuffer & mask_table[shn->nbitget]); + BAIL_IF_MACRO(!word_get(shn, rw, &shn->gbuffer), NULL, 0); + nbin -= shn->nbitget; + shn->nbitget = 32; + } /* else */ + } /* while */ + + if (word != NULL) + *word = result; + + return 1; +} /* uvar_get */ + + +static int var_get(int nbin, shn_t *shn, SDL_RWops *rw, Sint32 *word) +{ + BAIL_IF_MACRO(!uvar_get(nbin + 1, shn, rw, word), NULL, 0); + + if ((*word) & 1) + *word = (Sint32) ~((*word) >> 1); + else + *word = (Sint32) ((*word) >> 1); + + return 1; +} /* var_get */ + + +static int ulong_get(shn_t *shn, SDL_RWops *rw, Sint32 *word) +{ + Sint32 nbit; + Sint32 retval; + BAIL_IF_MACRO(!uvar_get(SHN_ULONGSIZE, shn, rw, &nbit), NULL, 0); + BAIL_IF_MACRO(!uvar_get(nbit, shn, rw, &retval), NULL, 0); + + if (word != NULL) + *word = retval; + + return 1; +} /* ulong_get */ + + +static SDL_INLINE int uint_get(int nbit, shn_t *shn, SDL_RWops *rw, Sint32 *w) +{ + return (shn->version == 0) ? uvar_get(nbit, shn, rw, w) : ulong_get(shn, rw, w); +} /* uint_get */ + + +static int SHN_init(void) +{ + return 1; /* initialization always successful. */ +} /* SHN_init */ + + +static void SHN_quit(void) +{ + /* it's a no-op. */ +} /* SHN_quit */ + + +/* + * Look through the whole file for a SHN magic number. This is costly, so + * it should only be done if the user SWEARS they have a Shorten stream... + */ +static SDL_INLINE int extended_shn_magic_search(Sound_Sample *sample) +{ + SDL_RWops *rw = ((Sound_SampleInternal *) sample->opaque)->rw; + Uint32 word = 0; + Uint8 ch; + + while (1) + { + BAIL_IF_MACRO(SDL_RWread(rw, &ch, sizeof (ch), 1) != 1, NULL, -1); + word = ((word << 8) & 0xFFFFFF00) | ch; + if (SDL_SwapBE32(word) == SHN_MAGIC) + { + BAIL_IF_MACRO(SDL_RWread(rw, &ch, sizeof (ch), 1) != 1, NULL, -1); + return (int) ch; + } /* if */ + } /* while */ + + return (int) ch; +} /* extended_shn_magic_search */ + + +/* look for the magic number in the RWops and see what kind of file this is. */ +static SDL_INLINE int determine_shn_version(Sound_Sample *sample, + const char *ext) +{ + SDL_RWops *rw = ((Sound_SampleInternal *) sample->opaque)->rw; + Uint32 magic; + Uint8 ch; + + /* + * Apparently the magic number can start at any byte offset in the file, + * and we should just discard prior data, but I'm going to restrict it + * to offset zero for now, so we don't chug down every file that might + * happen to pass through here. If the extension is explicitly "SHN", we + * check the whole stream, though. + */ + + if (SDL_strcasecmp(ext, "shn") == 0) + return extended_shn_magic_search(sample); + + BAIL_IF_MACRO(SDL_RWread(rw, &magic, sizeof (magic), 1) != 1, NULL, -1); + BAIL_IF_MACRO(SDL_SwapLE32(magic) != SHN_MAGIC, "SHN: Not a SHN file", -1); + BAIL_IF_MACRO(SDL_RWread(rw, &ch, sizeof (ch), 1) != 1, NULL, -1); + BAIL_IF_MACRO(ch > 3, "SHN: Unsupported file version", -1); + + return (int) ch; +} /* determine_shn_version */ + + +static void init_shn_offset(Sint32 **offset, int nchan, int nblock, int ftype) +{ + Sint32 mean = 0; + int chan; + + switch (ftype) + { + case SHN_TYPE_AU1: + case SHN_TYPE_S8: + case SHN_TYPE_S16HL: + case SHN_TYPE_S16LH: + case SHN_TYPE_ULAW: + case SHN_TYPE_AU2: + case SHN_TYPE_AU3: + case SHN_TYPE_ALAW: + mean = 0; + break; + case SHN_TYPE_U8: + mean = 0x80; + break; + case SHN_TYPE_U16HL: + case SHN_TYPE_U16LH: + mean = 0x8000; + break; + default: + __Sound_SetError("SHN: unknown file type"); + return; + } /* switch */ + + for(chan = 0; chan < nchan; chan++) + { + int i; + for(i = 0; i < nblock; i++) + offset[chan][i] = mean; + } /* for */ +} /* init_shn_offset */ + + +static SDL_INLINE Uint16 cvt_shnftype_to_sdlfmt(Sint16 shntype) +{ + switch (shntype) + { + case SHN_TYPE_S8: + return AUDIO_S8; + + case SHN_TYPE_ALAW: + case SHN_TYPE_ULAW: + case SHN_TYPE_AU1: + case SHN_TYPE_AU2: + case SHN_TYPE_AU3: + case SHN_TYPE_U8: + return AUDIO_U8; + + case SHN_TYPE_S16HL: + return AUDIO_S16MSB; + + case SHN_TYPE_S16LH: + return AUDIO_S16LSB; + + case SHN_TYPE_U16HL: + return AUDIO_U16MSB; + + case SHN_TYPE_U16LH: + return AUDIO_U16LSB; + } /* switch */ + + return 0; +} /* cvt_shnftype_to_sdlfmt */ + + +static SDL_INLINE int skip_bits(shn_t *shn, SDL_RWops *rw) +{ + int i; + Sint32 skip; + Sint32 trash; + + BAIL_IF_MACRO(!uint_get(SHN_NSKIPSIZE, shn, rw, &skip), NULL, 0); + for(i = 0; i < skip; i++) + { + BAIL_IF_MACRO(!uint_get(SHN_XBYTESIZE, shn, rw, &trash), NULL, 0); + } /* for */ + + return 1; +} /* skip_bits */ + + +static Sint32 **shn_long2d(Uint32 n0, Uint32 n1) +{ + Sint32 **array0; + Uint32 size = (n0 * sizeof (Sint32 *)) + (n0 * n1 * sizeof (Sint32)); + + array0 = (Sint32 **) SDL_malloc(size); + if (array0 != NULL) + { + int i; + Sint32 *array1 = (Sint32 *) (array0 + n0); + for(i = 0; i < n0; i++) + array0[i] = array1 + (i * n1); + } /* if */ + + return array0; +} /* shn_long2d */ + +#define riffID 0x46464952 /* "RIFF", in ascii. */ +#define waveID 0x45564157 /* "WAVE", in ascii. */ +#define fmtID 0x20746D66 /* "fmt ", in ascii. */ +#define dataID 0x61746164 /* "data", in ascii. */ + +static int verb_ReadLE32(shn_t *shn, SDL_RWops *rw, Uint32 *word) +{ + int i; + Uint8 chars[4]; + Sint32 byte; + + for (i = 0; i < 4; i++) + { + if (!uvar_get(SHN_VERBATIM_BYTE_SIZE, shn, rw, &byte)) + return 0; + chars[i] = (Uint8) byte; + } /* for */ + + SDL_memcpy(word, chars, sizeof (*word)); + *word = SDL_SwapLE32(*word); + + return 1; +} /* verb_ReadLE32 */ + + +static int verb_ReadLE16(shn_t *shn, SDL_RWops *rw, Uint16 *word) +{ + int i; + Uint8 chars[2]; + Sint32 byte; + + for (i = 0; i < 2; i++) + { + if (!uvar_get(SHN_VERBATIM_BYTE_SIZE, shn, rw, &byte)) + return 0; + chars[i] = (Uint8) byte; + } /* for */ + + SDL_memcpy(word, chars, sizeof (*word)); + *word = SDL_SwapLE16(*word); + + return 1; +} /* verb_ReadLE16 */ + + +static SDL_INLINE int parse_riff_header(shn_t *shn, Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + Uint16 u16; + Uint32 u32; + Sint32 cklen; + Uint32 bytes_per_second; + + BAIL_IF_MACRO(!uvar_get(SHN_VERBATIM_CKSIZE_SIZE, shn, rw, &cklen), NULL, 0); + + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* RIFF header */ + BAIL_IF_MACRO(u32 != riffID, "SHN: No RIFF header.", 0); + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* length */ + + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* WAVE header */ + BAIL_IF_MACRO(u32 != waveID, "SHN: No WAVE header.", 0); + + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* 'fmt ' header */ + BAIL_IF_MACRO(u32 != fmtID, "SHN: No 'fmt ' header.", 0); + + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* chunksize */ + BAIL_IF_MACRO(!verb_ReadLE16(shn, rw, &u16), NULL, 0); /* format */ + BAIL_IF_MACRO(!verb_ReadLE16(shn, rw, &u16), NULL, 0); /* channels */ + sample->actual.channels = u16; + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* sample rate */ + sample->actual.rate = u32; + + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* bytespersec */ + bytes_per_second = u32; + BAIL_IF_MACRO(!verb_ReadLE16(shn, rw, &u16), NULL, 0); /* blockalign */ + BAIL_IF_MACRO(!verb_ReadLE16(shn, rw, &u16), NULL, 0); /* bitspersample */ + + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* 'data' header */ + BAIL_IF_MACRO(u32 != dataID, "SHN: No 'data' header.", 0); + BAIL_IF_MACRO(!verb_ReadLE32(shn, rw, &u32), NULL, 0); /* chunksize */ + internal->total_time = u32 / bytes_per_second * 1000; + internal->total_time += (u32 % bytes_per_second) * 1000 / bytes_per_second; + return 1; +} /* parse_riff_header */ + + +static int SHN_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + shn_t _shn; + shn_t *shn = &_shn; /* malloc and copy later. */ + Sint32 cmd; + Sint32 chan; + + SDL_memset(shn, '\0', sizeof (shn_t)); + shn->getbufp = shn->getbuf = (Uint8 *) SDL_malloc(SHN_BUFSIZ); + shn->datatype = SHN_TYPE_EOF; + shn->nchan = DEFAULT_NCHAN; + shn->blocksize = DEFAULT_BLOCK_SIZE; + shn->maxnlpc = DEFAULT_MAXNLPC; + shn->nmean = UNDEFINED_UINT; + shn->version = determine_shn_version(sample, ext); + + if (shn->version == -1) goto shn_open_puke; + if (!uint_get(SHN_TYPESIZE, shn, rw, &shn->datatype)) goto shn_open_puke; + if (!uint_get(SHN_CHANNELSIZE, shn, rw, &shn->nchan)) goto shn_open_puke; + + sample->actual.format = cvt_shnftype_to_sdlfmt(shn->datatype); + if (sample->actual.format == 0) + { + SDL_SetError(ERR_UNSUPPORTED_FORMAT); + goto shn_open_puke; + } /* if */ + + if (shn->version > 0) + { + int rc = uint_get((int) (SDL_log((double) DEFAULT_BLOCK_SIZE) / M_LN2), + shn, rw, &shn->blocksize); + if (!rc) goto shn_open_puke;; + if (!uint_get(SHN_LPCQSIZE, shn, rw, &shn->maxnlpc)) goto shn_open_puke; + if (!uint_get(0, shn, rw, &shn->nmean)) goto shn_open_puke; + if (!skip_bits(shn, rw)) goto shn_open_puke; + } /* else */ + + shn->nwrap = (shn->maxnlpc > 3) ? shn->maxnlpc : 3; + + /* grab some space for the input buffer */ + shn->buffer = shn_long2d((Uint32) shn->nchan, shn->blocksize + shn->nwrap); + shn->offset = shn_long2d((Uint32) shn->nchan, MAX_MACRO(1, shn->nmean)); + + for (chan = 0; chan < shn->nchan; chan++) + { + int i; + for(i = 0; i < shn->nwrap; i++) + shn->buffer[chan][i] = 0; + shn->buffer[chan] += shn->nwrap; + } /* for */ + + if (shn->maxnlpc > 0) + { + shn->qlpc = (int *) SDL_malloc((Uint32) (shn->maxnlpc * sizeof (Sint32))); + if (shn->qlpc == NULL) + { + __Sound_SetError(ERR_OUT_OF_MEMORY); + goto shn_open_puke; + } /* if */ + } /* if */ + + if (shn->version > 1) + shn->lpcqoffset = SHN_LPCQOFFSET_VER2; + + init_shn_offset(shn->offset, shn->nchan, + MAX_MACRO(1, shn->nmean), shn->datatype); + + if ( (!uvar_get(SHN_FNSIZE, shn, rw, &cmd)) || + (cmd != SHN_FN_VERBATIM) || + (!parse_riff_header(shn, sample)) ) + { + if (cmd != SHN_FN_VERBATIM) /* the other conditions set error state */ + __Sound_SetError("SHN: Expected VERBATIM function"); + + goto shn_open_puke; + return 0; + } /* if */ + + shn->start_pos = SDL_RWtell(rw); + + shn = (shn_t *) SDL_malloc(sizeof (shn_t)); + if (shn == NULL) + { + __Sound_SetError(ERR_OUT_OF_MEMORY); + goto shn_open_puke; + } /* if */ + + SDL_memcpy(shn, &_shn, sizeof (shn_t)); + internal->decoder_private = shn; + + SNDDBG(("SHN: Accepting data stream.\n")); + sample->flags = SOUND_SAMPLEFLAG_NONE; + return 1; /* we'll handle this data. */ + +shn_open_puke: + if (_shn.getbuf) + SDL_free(_shn.getbuf); + if (_shn.buffer != NULL) + SDL_free(_shn.buffer); + if (_shn.offset != NULL) + SDL_free(_shn.offset); + if (_shn.qlpc != NULL) + SDL_free(_shn.qlpc); + + return 0; +} /* SHN_open */ + + +static void fix_bitshift(Sint32 *buffer, int nitem, int bitshift, int ftype) +{ + int i; + + if (ftype == SHN_TYPE_AU1) + { + for (i = 0; i < nitem; i++) + buffer[i] = ulaw_outward[bitshift][buffer[i] + 128]; + } /* if */ + else if (ftype == SHN_TYPE_AU2) + { + for(i = 0; i < nitem; i++) + { + if (buffer[i] >= 0) + buffer[i] = ulaw_outward[bitshift][buffer[i] + 128]; + else if (buffer[i] == -1) + buffer[i] = NEGATIVE_ULAW_ZERO; + else + buffer[i] = ulaw_outward[bitshift][buffer[i] + 129]; + } /* for */ + } /* else if */ + else + { + if (bitshift != 0) + { + for(i = 0; i < nitem; i++) + buffer[i] <<= bitshift; + } /* if */ + } /* else */ +} /* fix_bitshift */ + + +static void SHN_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + shn_t *shn = (shn_t *) internal->decoder_private; + + if (shn->qlpc != NULL) + SDL_free(shn->qlpc); + + if (shn->backBuffer != NULL) + SDL_free(shn->backBuffer); + + if (shn->offset != NULL) + SDL_free(shn->offset); + + if (shn->buffer != NULL) + SDL_free(shn->buffer); + + if (shn->getbuf != NULL) + SDL_free(shn->getbuf); + + SDL_free(shn); +} /* SHN_close */ + + +/* xLaw conversions... */ + +/* adapted by ajr for int input */ +static Uint8 Slinear2ulaw(int sample) +{ +/* +** This routine converts from linear to ulaw. +** +** Craig Reese: IDA/Supercomputing Research Center +** Joe Campbell: Department of Defense +** 29 September 1989 +** +** References: +** 1) CCITT Recommendation G.711 (very difficult to follow) +** 2) "A New Digital Technique for Implementation of Any +** Continuous PCM Companding Law," Villeret, Michel, +** et al. 1973 IEEE Int. Conf. on Communications, Vol 1, +** 1973, pg. 11.12-11.17 +** 3) MIL-STD-188-113,"Interoperability and Performance Standards +** for Analog-to_Digital Conversion Techniques," +** 17 February 1987 +** +** Input: Signed 16 bit linear sample +** Output: 8 bit ulaw sample +*/ + +#define BIAS 0x84 /* define the add-in bias for 16 bit samples */ +#define CLIP 32635 + + int sign, exponent, mantissa; + Uint8 ulawbyte; + static const int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3, + 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7}; + + /* Get the sample into sign-magnitude. */ + if (sample >= 0) + sign = 0; + else + { + sign = 0x80; + sample = -sample; + } /* else */ + + /* clip the magnitude */ + if (sample > CLIP) + sample = CLIP; + + /* Convert from 16 bit linear to ulaw. */ + sample = sample + BIAS; + exponent = exp_lut[( sample >> 7 ) & 0xFF]; + mantissa = (sample >> (exponent + 3)) & 0x0F; + ulawbyte = ~(sign | (exponent << 4) | mantissa); + + return ulawbyte; +} /* Slinear2ulaw */ + + +/* this is derived from the Sun code - it is a bit simpler and has int input */ +#define QUANT_MASK (0xf) /* Quantization field mask. */ +#define NSEGS (8) /* Number of A-law segments. */ +#define SEG_SHIFT (4) /* Left shift for segment number. */ + + +static Uint8 Slinear2alaw(Sint32 linear) +{ + int seg; + Uint8 aval, mask; + static const Sint32 seg_aend[NSEGS] = + { + 0x1f,0x3f,0x7f,0xff,0x1ff,0x3ff,0x7ff,0xfff + }; + + linear >>= 3; + if(linear >= 0) + mask = 0xd5; /* sign (7th) bit = 1 */ + else + { + mask = 0x55; /* sign bit = 0 */ + linear = -linear - 1; + } /* else */ + + /* Convert the scaled magnitude to segment number. */ + for (seg = 0; (seg < NSEGS) && (linear > seg_aend[seg]); seg++); + + /* Combine the sign, segment, and quantization bits. */ + if (seg >= NSEGS) /* out of range, return maximum value. */ + return (Uint8) (0x7F ^ mask); + + aval = (Uint8) seg << SEG_SHIFT; + if (seg < 2) + aval |= (linear >> 1) & QUANT_MASK; + else + aval |= (linear >> seg) & QUANT_MASK; + + return (aval ^ mask); +} /* Slinear2alaw */ + + +/* convert from signed ints to a given type and write */ +static Uint32 put_to_buffers(Sound_Sample *sample, Uint32 bw) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + shn_t *shn = (shn_t *) internal->decoder_private; + int i, chan; + Sint32 *data0 = shn->buffer[0]; + Sint32 nitem = shn->blocksize; + int datasize = ((sample->actual.format & 0xFF) / 8); + Uint32 bsiz = shn->nchan * nitem * datasize; + + SDL_assert(shn->backBufLeft == 0); + + if (shn->backBufferSize < bsiz) + { + void *rc = SDL_realloc(shn->backBuffer, bsiz); + if (rc == NULL) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + BAIL_MACRO(ERR_OUT_OF_MEMORY, 0); + } /* if */ + shn->backBuffer = (Uint8 *) rc; + shn->backBufferSize = bsiz; + } /* if */ + + switch (shn->datatype) + { + case SHN_TYPE_AU1: /* leave the conversion to fix_bitshift() */ + case SHN_TYPE_AU2: + { + Uint8 *writebufp = shn->backBuffer; + if (shn->nchan == 1) + { + for (i = 0; i < nitem; i++) + *writebufp++ = data0[i]; + } /* if */ + else + { + for (i = 0; i < nitem; i++) + { + for (chan = 0; chan < shn->nchan; chan++) + *writebufp++ = shn->buffer[chan][i]; + } /* for */ + } /* else */ + } /* case */ + break; + + case SHN_TYPE_U8: + { + Uint8 *writebufp = shn->backBuffer; + if (shn->nchan == 1) + { + for (i = 0; i < nitem; i++) + *writebufp++ = CAPMAXUCHAR(data0[i]); + } /* if */ + else + { + for (i = 0; i < nitem; i++) + { + for (chan = 0; chan < shn->nchan; chan++) + *writebufp++ = CAPMAXUCHAR(shn->buffer[chan][i]); + } /* for */ + } /* else */ + } /* case */ + break; + + case SHN_TYPE_S8: + { + Sint8 *writebufp = (Sint8 *) shn->backBuffer; + if (shn->nchan == 1) + { + for(i = 0; i < nitem; i++) + *writebufp++ = CAPMAXSCHAR(data0[i]); + } /* if */ + else + { + for(i = 0; i < nitem; i++) + { + for(chan = 0; chan < shn->nchan; chan++) + *writebufp++ = CAPMAXSCHAR(shn->buffer[chan][i]); + } /* for */ + } /* else */ + } /* case */ + break; + + case SHN_TYPE_S16HL: + case SHN_TYPE_S16LH: + { + Sint16 *writebufp = (Sint16 *) shn->backBuffer; + if (shn->nchan == 1) + { + for (i = 0; i < nitem; i++) + *writebufp++ = CAPMAXSHORT(data0[i]); + } /* if */ + else + { + for (i = 0; i < nitem; i++) + { + for (chan = 0; chan < shn->nchan; chan++) + *writebufp++ = CAPMAXSHORT(shn->buffer[chan][i]); + } /* for */ + } /* else */ + } /* case */ + break; + + case SHN_TYPE_U16HL: + case SHN_TYPE_U16LH: + { + Uint16 *writebufp = (Uint16 *) shn->backBuffer; + if (shn->nchan == 1) + { + for (i = 0; i < nitem; i++) + *writebufp++ = CAPMAXUSHORT(data0[i]); + } /* if */ + else + { + for (i = 0; i < nitem; i++) + { + for (chan = 0; chan < shn->nchan; chan++) + *writebufp++ = CAPMAXUSHORT(shn->buffer[chan][i]); + } /* for */ + } /* else */ + } /* case */ + break; + + case SHN_TYPE_ULAW: + { + Uint8 *writebufp = shn->backBuffer; + if (shn->nchan == 1) + { + for(i = 0; i < nitem; i++) + *writebufp++ = Slinear2ulaw(CAPMAXSHORT((data0[i] << 3))); + } /* if */ + else + { + for(i = 0; i < nitem; i++) + { + for(chan = 0; chan < shn->nchan; chan++) + *writebufp++ = Slinear2ulaw(CAPMAXSHORT((shn->buffer[chan][i] << 3))); + } /* for */ + } /* else */ + } /* case */ + break; + + case SHN_TYPE_AU3: + { + Uint8 *writebufp = shn->backBuffer; + if (shn->nchan == 1) + { + for (i = 0; i < nitem; i++) + if(data0[i] < 0) + *writebufp++ = (127 - data0[i]) ^ 0xd5; + else + *writebufp++ = (data0[i] + 128) ^ 0x55; + } /* if */ + else + { + for (i = 0; i < nitem; i++) + { + for (chan = 0; chan < shn->nchan; chan++) + { + if (shn->buffer[chan][i] < 0) + *writebufp++ = (127 - shn->buffer[chan][i]) ^ 0xd5; + else + *writebufp++ = (shn->buffer[chan][i] + 128) ^ 0x55; + } /* for */ + } /* for */ + } /* else */ + } /* case */ + break; + + case SHN_TYPE_ALAW: + { + Uint8 *writebufp = shn->backBuffer; + if (shn->nchan == 1) + { + for (i = 0; i < nitem; i++) + *writebufp++ = Slinear2alaw(CAPMAXSHORT((data0[i] << 3))); + } /* if */ + else + { + for (i = 0; i < nitem; i++) + { + for(chan = 0; chan < shn->nchan; chan++) + *writebufp++ = Slinear2alaw(CAPMAXSHORT((shn->buffer[chan][i] << 3))); + } /* for */ + }/* else */ + } /* case */ + break; + } /* switch */ + + i = MIN_MACRO(internal->buffer_size - bw, bsiz); + SDL_memcpy((char *)internal->buffer + bw, shn->backBuffer, i); + shn->backBufLeft = bsiz - i; + SDL_memcpy(shn->backBuffer, shn->backBuffer + i, shn->backBufLeft); + return i; +} /* put_to_buffers */ + + +#define ROUNDEDSHIFTDOWN(x, n) (((n) == 0) ? (x) : ((x) >> ((n) - 1)) >> 1) + +static Uint32 SHN_read(Sound_Sample *sample) +{ + Uint32 retval = 0; + Sint32 chan = 0; + Uint32 cpyBytes = 0; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + shn_t *shn = (shn_t *) internal->decoder_private; + Sint32 cmd; + + SDL_assert(shn->backBufLeft >= 0); + + /* see if there are leftovers to copy... */ + if (shn->backBufLeft > 0) + { + retval = MIN_MACRO(shn->backBufLeft, internal->buffer_size); + SDL_memcpy(internal->buffer, shn->backBuffer, retval); + shn->backBufLeft -= retval; + SDL_memcpy(shn->backBuffer, shn->backBuffer + retval, shn->backBufLeft); + } /* if */ + + SDL_assert((shn->backBufLeft == 0) || (retval == internal->buffer_size)); + + /* get commands from file and execute them */ + while (retval < internal->buffer_size) + { + if (!uvar_get(SHN_FNSIZE, shn, rw, &cmd)) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + + if (cmd == SHN_FN_QUIT) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + return retval; + } /* if */ + + switch(cmd) + { + case SHN_FN_ZERO: + case SHN_FN_DIFF0: + case SHN_FN_DIFF1: + case SHN_FN_DIFF2: + case SHN_FN_DIFF3: + case SHN_FN_QLPC: + { + Sint32 i; + Sint32 coffset, *cbuffer = shn->buffer[chan]; + Sint32 resn = 0, nlpc, j; + + if (cmd != SHN_FN_ZERO) + { + if (!uvar_get(SHN_ENERGYSIZE, shn, rw, &resn)) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + + /* version 0 differed in definition of var_get */ + if (shn->version == 0) + resn--; + } /* if */ + + /* find mean offset : N.B. this code duplicated */ + if (shn->nmean == 0) + coffset = shn->offset[chan][0]; + else + { + Sint32 sum = (shn->version < 2) ? 0 : shn->nmean / 2; + for (i = 0; i < shn->nmean; i++) + sum += shn->offset[chan][i]; + + if (shn->version < 2) + coffset = sum / shn->nmean; + else + coffset = ROUNDEDSHIFTDOWN(sum / shn->nmean, shn->bitshift); + } /* else */ + + switch (cmd) + { + case SHN_FN_ZERO: + for (i = 0; i < shn->blocksize; i++) + cbuffer[i] = 0; + break; + + case SHN_FN_DIFF0: + for(i = 0; i < shn->blocksize; i++) + { + if (!var_get(resn, shn, rw, &cbuffer[i])) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + cbuffer[i] += coffset; + } /* for */ + break; + + case SHN_FN_DIFF1: + for(i = 0; i < shn->blocksize; i++) + { + if (!var_get(resn, shn, rw, &cbuffer[i])) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + cbuffer[i] += cbuffer[i - 1]; + } /* for */ + break; + + case SHN_FN_DIFF2: + for (i = 0; i < shn->blocksize; i++) + { + if (!var_get(resn, shn, rw, &cbuffer[i])) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + cbuffer[i] += (2 * cbuffer[i-1] - cbuffer[i-2]); + } /* for */ + break; + + case SHN_FN_DIFF3: + for (i = 0; i < shn->blocksize; i++) + { + if (!var_get(resn, shn, rw, &cbuffer[i])) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + cbuffer[i] += 3 * (cbuffer[i - 1] - cbuffer[i - 2]) + cbuffer[i - 3]; + } /* for */ + break; + + case SHN_FN_QLPC: + if (!uvar_get(SHN_LPCQSIZE, shn, rw, &nlpc)) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + + for(i = 0; i < nlpc; i++) + { + if (!var_get(SHN_LPCQUANT, shn, rw, &shn->qlpc[i])) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + } /* for */ + + for(i = 0; i < nlpc; i++) + cbuffer[i - nlpc] -= coffset; + + for(i = 0; i < shn->blocksize; i++) + { + Sint32 sum = shn->lpcqoffset; + + for(j = 0; j < nlpc; j++) + sum += shn->qlpc[j] * cbuffer[i - j - 1]; + + if (!var_get(resn, shn, rw, &cbuffer[i])) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + cbuffer[i] += (sum >> SHN_LPCQUANT); + } /* for */ + + if (coffset != 0) + { + for(i = 0; i < shn->blocksize; i++) + cbuffer[i] += coffset; + } /* if */ + + break; + } /* switch */ + + /* store mean value if appropriate : N.B. Duplicated code */ + if (shn->nmean > 0) + { + Sint32 sum = (shn->version < 2) ? 0 : shn->blocksize / 2; + for (i = 0; i < shn->blocksize; i++) + sum += cbuffer[i]; + + for(i = 1; i < shn->nmean; i++) + shn->offset[chan][i - 1] = shn->offset[chan][i]; + + if (shn->version < 2) + shn->offset[chan][shn->nmean - 1] = sum / shn->blocksize; + else + shn->offset[chan][shn->nmean - 1] = (sum / shn->blocksize) << shn->bitshift; + } /* if */ + + /* do the wrap */ + for(i = -shn->nwrap; i < 0; i++) + cbuffer[i] = cbuffer[i + shn->blocksize]; + + fix_bitshift(cbuffer, shn->blocksize, shn->bitshift, shn->datatype); + + if (chan == shn->nchan - 1) + { + retval += put_to_buffers(sample, retval); + if (sample->flags & SOUND_SAMPLEFLAG_ERROR) + return retval; + } /* if */ + + chan = (chan + 1) % shn->nchan; + break; + } /* case */ + + case SHN_FN_BLOCKSIZE: + if (!uint_get((int) (SDL_log((double) shn->blocksize) / M_LN2), + shn, rw, &shn->blocksize)) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + break; + + case SHN_FN_BITSHIFT: + if (!uvar_get(SHN_BITSHIFTSIZE, shn, rw, &shn->bitshift)) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return retval; + } /* if */ + break; + + case SHN_FN_VERBATIM: + default: + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + BAIL_MACRO("SHN: Unhandled function.", retval); + } /* switch */ + } /* while */ + + return retval; +} /* SHN_read */ + + +static int SHN_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + shn_t *shn = (shn_t *) internal->decoder_private; + +#if 0 + int rc = SDL_RWseek(internal->rw, shn->start_pos, SEEK_SET); + BAIL_IF_MACRO(rc != shn->start_pos, ERR_IO_ERROR, 0); + /* !!! FIXME: set state. */ + return 1; +#else + /* + * !!! FIXME: This is really unacceptable; state should be reset and + * !!! FIXME: the RWops should be pointed to the start of the data + * !!! FIXME: to decode. The below kludge adds unneeded overhead and + * !!! FIXME: risk of failure. + */ + BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0); + SHN_close(sample); + return SHN_open(sample, "SHN"); +#endif +} /* SHN_rewind */ + + +static int SHN_seek(Sound_Sample *sample, Uint32 ms) +{ + /* + * (This CAN be done for SHNs that have a seek table at the end of the + * stream, btw.) + */ + BAIL_MACRO("SHN: Seeking not implemented", 0); +} /* SHN_seek */ + + +static const char *extensions_shn[] = { "SHN", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_SHN = +{ + { + extensions_shn, + "Shorten-compressed audio data", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + SHN_init, /* init() method */ + SHN_quit, /* quit() method */ + SHN_open, /* open() method */ + SHN_close, /* close() method */ + SHN_read, /* read() method */ + SHN_rewind, /* rewind() method */ + SHN_seek /* seek() method */ +}; + +#endif /* defined SOUND_SUPPORTS_SHN */ + +/* end of SDL_sound_shn.c ... */ + diff --git a/SDL2_sound/SDL_sound_skeleton.c b/SDL2_sound/SDL_sound_skeleton.c new file mode 100644 index 00000000..8129d3b4 --- /dev/null +++ b/SDL2_sound/SDL_sound_skeleton.c @@ -0,0 +1,144 @@ +/** + * SDL_sound; A sound processing toolkit. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * FMT decoder for SDL_sound. + * + * This driver handles FMT audio data. Blahblahblah... The author should + * have done a search and replace on "fmt" and "FMT" and changed this + * comment. This is the default comment in the skeleton decoder file... + * + * None of this code, even the parts that LOOK right, have been compiled, + * so you cut-and-paste at your own risk. + */ + +#error DO NOT COMPILE THIS. +#error This is an example decoder skeleton. +#error You should base your code on this file, and remove these error lines +#error from your version. + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_FMT + +static int FMT_init(void) +{ + /* do any global decoder/library initialization you need here. */ + + return 1; /* initialization successful. */ +} /* FMT_init */ + + +static void FMT_quit(void) +{ + /* do any global decoder/library cleanup you need here. */ +} /* FMT_quit */ + + +static int FMT_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + + if (can NOT accept the data) + BAIL_MACRO("FMT: expected X, got Y.", 0); + + SNDDBG(("FMT: Accepting data stream.\n")); + set up sample->actual; + sample->flags = SOUND_SAMPLEFLAG_NONE; + return 1; /* we'll handle this data. */ +} /* FMT_open */ + + +static void FMT_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + clean up anything you put into internal->decoder_private; +} /* FMT_close */ + + +static Uint32 FMT_read(Sound_Sample *sample) +{ + Uint32 retval; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + + /* + * We don't actually do any decoding, so we read the fmt data + * directly into the internal buffer... + */ + retval = SDL_RWread(internal->rw, internal->buffer, + 1, internal->buffer_size); + + (or whatever. Do some decoding here...) + + /* Make sure the read went smoothly... */ + if (retval == 0) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + + else if (retval == -1) + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + + /* (next call this EAGAIN may turn into an EOF or error.) */ + else if (retval < internal->buffer_size) + sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + + (or whatever. retval == number of bytes you put in internal->buffer). + + return retval; +} /* FMT_read */ + + +static int FMT_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + + /* seek to the appropriate place... */ + BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0); + + (reset state as necessary.) + + return 1; /* success. */ +} /* FMT_rewind */ + + +static int FMT_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + + /* seek to the appropriate place... */ + BAIL_IF_MACRO(SDL_RWseek(internal->rw, 0, SEEK_SET) != 0, ERR_IO_ERROR, 0); + + (set state as necessary.) + + return 1; /* success. */ +} /* FMT_seek */ + +static const char *extensions_fmt[] = { "FMT", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_FMT = +{ + { + extensions_fmt, + "FMT audio format description", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + FMT_init, /* init() method */ + FMT_quit, /* quit() method */ + FMT_open, /* open() method */ + FMT_close, /* close() method */ + FMT_read, /* read() method */ + FMT_rewind, /* rewind() method */ + FMT_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_FMT */ + +/* end of SDL_sound_fmt.c ... */ + diff --git a/SDL2_sound/SDL_sound_voc.c b/SDL2_sound/SDL_sound_voc.c new file mode 100644 index 00000000..b2643725 --- /dev/null +++ b/SDL2_sound/SDL_sound_voc.c @@ -0,0 +1,550 @@ +/** + * SDL_sound; A sound processing toolkit. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * VOC decoder for SDL_sound. + * + * This driver handles Creative Labs VOC audio data...this is a legacy format, + * but there's some game ports that could make use of such a decoder. Plus, + * VOC is fairly straightforward to decode, so this is a more complex, but + * still palatable example of an SDL_sound decoder. Y'know, in case the + * RAW decoder didn't do it for you. :) + * + * This code was ripped from a decoder I had written for SDL_mixer, which was + * largely ripped from sox v12.17.1's voc.c. + * + * SDL_mixer: https://www.libsdl.org/projects/SDL_mixer/ + * sox: http://www.freshmeat.net/projects/sox/ + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_VOC + +/* Private data for VOC file */ +typedef struct vocstuff { + Uint32 rest; /* bytes remaining in current block */ + Uint32 rate; /* rate code (byte) of this chunk */ + int silent; /* sound or silence? */ + Uint32 srate; /* rate code (byte) of silence */ + Uint32 blockseek; /* start of current output block */ + Uint32 samples; /* number of samples output */ + Uint32 size; /* word length of data */ + Uint8 channels; /* number of sound channels */ + int extended; /* Has an extended block been read? */ + Uint32 bufpos; /* byte position in internal->buffer. */ + Uint32 start_pos; /* offset to seek to in stream when rewinding. */ + int error; /* error condition (as opposed to EOF). */ +} vs_t; + + +/* Size field */ +/* SJB: note that the 1st 3 are sometimes used as sizeof(type) */ +#define ST_SIZE_BYTE 1 +#define ST_SIZE_8BIT 1 +#define ST_SIZE_WORD 2 +#define ST_SIZE_16BIT 2 +#define ST_SIZE_DWORD 4 +#define ST_SIZE_32BIT 4 +#define ST_SIZE_FLOAT 5 +#define ST_SIZE_DOUBLE 6 +#define ST_SIZE_IEEE 7 /* IEEE 80-bit floats. */ + +/* Style field */ +#define ST_ENCODING_UNSIGNED 1 /* unsigned linear: Sound Blaster */ +#define ST_ENCODING_SIGN2 2 /* signed linear 2's comp: Mac */ +#define ST_ENCODING_ULAW 3 /* U-law signed logs: US telephony, SPARC */ +#define ST_ENCODING_ALAW 4 /* A-law signed logs: non-US telephony */ +#define ST_ENCODING_ADPCM 5 /* Compressed PCM */ +#define ST_ENCODING_IMA_ADPCM 6 /* Compressed PCM */ +#define ST_ENCODING_GSM 7 /* GSM 6.10 33-byte frame lossy compression */ + +#define VOC_TERM 0 +#define VOC_DATA 1 +#define VOC_CONT 2 +#define VOC_SILENCE 3 +#define VOC_MARKER 4 +#define VOC_TEXT 5 +#define VOC_LOOP 6 +#define VOC_LOOPEND 7 +#define VOC_EXTENDED 8 +#define VOC_DATA_16 9 + + +static int VOC_init(void) +{ + return 1; /* always succeeds. */ +} /* VOC_init */ + + +static void VOC_quit(void) +{ + /* it's a no-op. */ +} /* VOC_quit */ + + +static SDL_INLINE int voc_readbytes(SDL_RWops *src, vs_t *v, void *p, int size) +{ + if (SDL_RWread(src, p, size, 1) != 1) + { + v->error = 1; + BAIL_MACRO("VOC: i/o error", 0); + } /* if */ + + return 1; +} /* voc_readbytes */ + + +static SDL_INLINE int voc_check_header(SDL_RWops *src) +{ + /* VOC magic header */ + Uint8 signature[20]; /* "Creative Voice File\032" */ + Uint16 datablockofs; + vs_t v; /* dummy struct for voc_readbytes */ + + if (!voc_readbytes(src, &v, signature, sizeof (signature))) + return 0; + + if (SDL_memcmp(signature, "Creative Voice File\032", sizeof (signature)) != 0) + { + BAIL_MACRO("VOC: Wrong signature; not a VOC file.", 0); + } /* if */ + + /* get the offset where the first datablock is located */ + if (!voc_readbytes(src, &v, &datablockofs, sizeof (Uint16))) + return 0; + + datablockofs = SDL_SwapLE16(datablockofs); + + if (SDL_RWseek(src, datablockofs, SEEK_SET) != datablockofs) + { + BAIL_MACRO("VOC: Failed to seek to data block.", 0); + } /* if */ + + return 1; /* success! */ +} /* voc_check_header */ + + +/* Read next block header, save info, leave position at start of data */ +static int voc_get_block(Sound_Sample *sample, vs_t *v) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *src = internal->rw; + Uint8 bits24[3]; + Uint8 uc, block; + Uint32 sblen; + Uint16 new_rate_short; + Uint32 new_rate_long; + Uint8 trash[6]; + Uint16 period; + Uint32 bytes_per_second; + int i; + + v->silent = 0; + while (v->rest == 0) + { + if (SDL_RWread(src, &block, sizeof (block), 1) != 1) + return 1; /* assume that's the end of the file. */ + + if (block == VOC_TERM) + return 1; + + if (SDL_RWread(src, bits24, sizeof (bits24), 1) != 1) + return 1; /* assume that's the end of the file. */ + + /* Size is an 24-bit value. Ugh. */ + sblen = ( (bits24[0]) | (bits24[1] << 8) | (bits24[2] << 16) ); + + switch(block) + { + case VOC_DATA: + if (!voc_readbytes(src, v, &uc, sizeof (uc))) + return 0; + + /* When DATA block preceeded by an EXTENDED */ + /* block, the DATA blocks rate value is invalid */ + if (!v->extended) + { + BAIL_IF_MACRO(uc == 0, "VOC: Sample rate is zero?", 0); + + if ((v->rate != -1) && (uc != v->rate)) + BAIL_MACRO("VOC sample rate codes differ", 0); + + v->rate = uc; + sample->actual.rate = 1000000.0/(256 - v->rate); + sample->actual.channels = 1; + v->channels = 1; + } /* if */ + + if (!voc_readbytes(src, v, &uc, sizeof (uc))) + return 0; + + BAIL_IF_MACRO(uc != 0, "VOC: only supports 8-bit data", 0); + + v->extended = 0; + v->rest = sblen - 2; + v->size = ST_SIZE_BYTE; + + bytes_per_second = sample->actual.rate + * sample->actual.channels; + internal->total_time += ( v->rest ) / bytes_per_second * 1000; + internal->total_time += (v->rest % bytes_per_second) * 1000 + / bytes_per_second; + return 1; + + case VOC_DATA_16: + if (!voc_readbytes(src, v, &new_rate_long, sizeof (Uint32))) + return 0; + + new_rate_long = SDL_SwapLE32(new_rate_long); + BAIL_IF_MACRO(!new_rate_long, "VOC: Sample rate is zero?", 0); + + if ((v->rate != -1) && (new_rate_long != v->rate)) + BAIL_MACRO("VOC: sample rate codes differ", 0); + + v->rate = new_rate_long; + sample->actual.rate = new_rate_long; + + if (!voc_readbytes(src, v, &uc, sizeof (uc))) + return 0; + + switch (uc) + { + case 8: v->size = ST_SIZE_BYTE; break; + case 16: v->size = ST_SIZE_WORD; break; + default: + BAIL_MACRO("VOC: unknown data size", 0); + } /* switch */ + + if (!voc_readbytes(src, v, &v->channels, sizeof (Uint8))) + return 0; + + if (!voc_readbytes(src, v, trash, sizeof (Uint8) * 6)) + return 0; + v->rest = sblen - 12; + + bytes_per_second = ((v->size == ST_SIZE_WORD) ? (2) : (1)) * + sample->actual.rate * v->channels; + internal->total_time += v->rest / bytes_per_second * 1000; + internal->total_time += ( v->rest % bytes_per_second ) * 1000 + / bytes_per_second; + return 1; + + case VOC_CONT: + v->rest = sblen; + return 1; + + case VOC_SILENCE: + if (!voc_readbytes(src, v, &period, sizeof (period))) + return 0; + + period = SDL_SwapLE16(period); + + if (!voc_readbytes(src, v, &uc, sizeof (uc))) + return 0; + + BAIL_IF_MACRO(uc == 0, "VOC: silence sample rate is zero", 0); + + /* + * Some silence-packed files have gratuitously + * different sample rate codes in silence. + * Adjust period. + */ + if ((v->rate != -1) && (uc != v->rate)) + period = (period * (256 - uc))/(256 - v->rate); + else + v->rate = uc; + v->rest = period; + v->silent = 1; + + internal->total_time += (period) / (v->rate) * 1000; + internal->total_time += (period % v->rate) * 1000 / v->rate; + return 1; + + case VOC_LOOP: + case VOC_LOOPEND: + for(i = 0; i < sblen; i++) /* skip repeat loops. */ + { + if (!voc_readbytes(src, v, trash, sizeof (Uint8))) + return 0; + } /* for */ + break; + + case VOC_EXTENDED: + /* An Extended block is followed by a data block */ + /* Set this byte so we know to use the rate */ + /* value from the extended block and not the */ + /* data block. */ + v->extended = 1; + if (!voc_readbytes(src, v, &new_rate_short, sizeof (Uint16))) + return 0; + + new_rate_short = SDL_SwapLE16(new_rate_short); + BAIL_IF_MACRO(!new_rate_short, "VOC: sample rate is zero", 0); + + if ((v->rate != -1) && (new_rate_short != v->rate)) + BAIL_MACRO("VOC: sample rate codes differ", 0); + + v->rate = new_rate_short; + + if (!voc_readbytes(src, v, &uc, sizeof (uc))) + return 0; + + BAIL_IF_MACRO(uc != 0, "VOC: only supports 8-bit data", 0); + + if (!voc_readbytes(src, v, &uc, sizeof (uc))) + return 0; + + if (uc) + sample->actual.channels = 2; /* Stereo */ + + /* Needed number of channels before finishing + compute for rate */ + sample->actual.rate = + (256000000L/(65536L - v->rate)) / sample->actual.channels; + /* An extended block must be followed by a data */ + /* block to be valid so loop back to top so it */ + /* can be grabed. */ + continue; + + case VOC_MARKER: + if (!voc_readbytes(src, v, trash, sizeof (Uint8) * 2)) + return 0; + + /* Falling! Falling! */ + + default: /* text block or other krapola. */ + for(i = 0; i < sblen; i++) /* skip repeat loops. */ + { + if (!voc_readbytes(src, v, trash, sizeof (Uint8))) + return 0; + } /* for */ + + if (block == VOC_TEXT) + continue; /* get next block */ + } /* switch */ + } /* while */ + + return 1; +} /* voc_get_block */ + + +static int voc_read_waveform(Sound_Sample *sample, int fill_buf, Uint32 max) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *src = internal->rw; + vs_t *v = (vs_t *) internal->decoder_private; + int done = 0; + Uint8 silence = 0x80; + Uint8 *buf = internal->buffer; + + if (v->rest == 0) + { + if (!voc_get_block(sample, v)) + return 0; + } /* if */ + + if (v->rest == 0) + return 0; + + max = (v->rest < max) ? v->rest : max; + + if (v->silent) + { + if (v->size == ST_SIZE_WORD) + silence = 0x00; + + /* Fill in silence */ + if (fill_buf) + SDL_memset(buf + v->bufpos, silence, max); + + done = max; + v->rest -= done; + } /* if */ + + else + { + if (fill_buf) + { + done = SDL_RWread(src, buf + v->bufpos, 1, max); + if (done < max) + { + __Sound_SetError("VOC: i/o error"); + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + } /* if */ + } /* if */ + + else + { + int cur, rc; + cur = SDL_RWtell(src); + if (cur >= 0) + { + rc = SDL_RWseek(src, max, SEEK_CUR); + if (rc >= 0) + done = rc - cur; + else + { + __Sound_SetError("VOC: seek error"); + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + } /* else */ + } /* if */ + } /* else */ + + v->rest -= done; + v->bufpos += done; + } /* else */ + + return done; +} /* voc_read_waveform */ + + +static int VOC_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + vs_t *v = NULL; + + if (!voc_check_header(internal->rw)) + return 0; + + v = (vs_t *) SDL_calloc(1, sizeof (vs_t)); + BAIL_IF_MACRO(v == NULL, ERR_OUT_OF_MEMORY, 0); + + v->start_pos = SDL_RWtell(internal->rw); + v->rate = -1; + if (!voc_get_block(sample, v)) + { + SDL_free(v); + return 0; + } /* if */ + + if (v->rate == -1) + { + SDL_free(v); + BAIL_MACRO("VOC: data had no sound!", 0); + } /* if */ + + SNDDBG(("VOC: Accepting data stream.\n")); + sample->actual.format = (v->size == ST_SIZE_WORD) ? AUDIO_S16LSB:AUDIO_U8; + sample->actual.channels = v->channels; + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + internal->decoder_private = v; + return 1; +} /* VOC_open */ + + +static void VOC_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_free(internal->decoder_private); +} /* VOC_close */ + + +static Uint32 VOC_read(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + vs_t *v = (vs_t *) internal->decoder_private; + + v->bufpos = 0; + while (v->bufpos < internal->buffer_size) + { + Uint32 rc = voc_read_waveform(sample, 1, internal->buffer_size); + if (rc == 0) + { + sample->flags |= (v->error) ? + SOUND_SAMPLEFLAG_ERROR : + SOUND_SAMPLEFLAG_EOF; + break; + } /* if */ + + if (!voc_get_block(sample, v)) + { + sample->flags |= (v->error) ? + SOUND_SAMPLEFLAG_ERROR : + SOUND_SAMPLEFLAG_EOF; + break; + } /* if */ + } /* while */ + + return v->bufpos; +} /* VOC_read */ + + +static int VOC_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + vs_t *v = (vs_t *) internal->decoder_private; + int rc = SDL_RWseek(internal->rw, v->start_pos, SEEK_SET); + BAIL_IF_MACRO(rc != v->start_pos, ERR_IO_ERROR, 0); + v->rest = 0; + return 1; +} /* VOC_rewind */ + + +static int VOC_seek(Sound_Sample *sample, Uint32 ms) +{ + /* + * VOCs don't lend themselves well to seeking, since you have to + * parse each section, which is an arbitrary size. The best we can do + * is rewind, set a flag saying not to write the waveforms to a buffer, + * and decode to the point that we want. Ugh. Fortunately, there's + * really no such thing as a large VOC, due to the era and hardware that + * spawned them, so even though this is inefficient, this is still a + * relatively fast operation in most cases. + */ + + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + vs_t *v = (vs_t *) internal->decoder_private; + int offset = __Sound_convertMsToBytePos(&sample->actual, ms); + int origpos = SDL_RWtell(internal->rw); + int origrest = v->rest; + + BAIL_IF_MACRO(!VOC_rewind(sample), NULL, 0); + + v->bufpos = 0; + + while (offset > 0) + { + Uint32 rc = voc_read_waveform(sample, 0, offset); + if ( (rc == 0) || (!voc_get_block(sample, v)) ) + { + SDL_RWseek(internal->rw, origpos, SEEK_SET); + v->rest = origrest; + return 0; + } /* if */ + + offset -= rc; + } /* while */ + + return 1; +} /* VOC_seek */ + + +static const char *extensions_voc[] = { "VOC", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_VOC = +{ + { + extensions_voc, + "Creative Labs Voice format", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + VOC_init, /* init() method */ + VOC_quit, /* quit() method */ + VOC_open, /* open() method */ + VOC_close, /* close() method */ + VOC_read, /* read() method */ + VOC_rewind, /* rewind() method */ + VOC_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_VOC */ + +/* end of SDL_sound_voc.c ... */ diff --git a/SDL2_sound/SDL_sound_vorbis.c b/SDL2_sound/SDL_sound_vorbis.c new file mode 100644 index 00000000..17110da8 --- /dev/null +++ b/SDL2_sound/SDL_sound_vorbis.c @@ -0,0 +1,223 @@ +/** + * SDL_sound; A sound processing toolkit. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * Ogg Vorbis decoder for SDL_sound. + * + * This driver handles .OGG audio files, and uses Sean Barrett's excellent + * stb_vorbis for the heavy lifting. Since stb_vorbis is a single, + * public domain C header file, it's just compiled into this source and + * needs no external dependencies. + * + * stb_vorbis homepage: https://nothings.org/stb_vorbis/ + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_VORBIS + +/* Configure and include stb_vorbis for compiling... */ +#define STB_VORBIS_NO_STDIO 1 +#define STB_VORBIS_NO_CRT 1 +#define STB_VORBIS_NO_PUSHDATA_API 1 +#define STB_VORBIS_MAX_CHANNELS 6 +#define STBV_CDECL +#define STB_FORCEINLINE SDL_FORCE_INLINE +#if SDL_BYTEORDER == SDL_BIG_ENDIAN +#define STB_VORBIS_BIG_ENDIAN 1 +#endif + +#if !defined(__clang_analyzer__) +#ifdef assert +#undef assert +#endif +#ifdef memset +#undef memset +#endif +#ifdef memcpy +#undef memcpy +#endif +#ifdef alloca +#undef alloca +#endif +#define assert SDL_assert +#define memset SDL_memset +#define memcmp SDL_memcmp +#define memcpy SDL_memcpy +#define qsort SDL_qsort +#define pow SDL_pow +#define floor SDL_floor +#define malloc SDL_malloc +#define realloc SDL_realloc +#define free SDL_free +#define alloca(x) ((void *) SDL_stack_alloc(Uint8, (x))) +#define dealloca(x) SDL_stack_free((x)) +#define ldexp(v, e) SDL_scalbn((v), (e)) +#define abs(x) SDL_abs(x) +#define cos(x) SDL_cos(x) +#define sin(x) SDL_sin(x) +#define log(x) SDL_log(x) +#endif +#include "stb_vorbis.h" + +static const char *vorbis_error_string(const int err) +{ + switch (err) + { + case VORBIS__no_error: return NULL; + case VORBIS_need_more_data: return "VORBIS: need more data"; + case VORBIS_invalid_api_mixing: return "VORBIS: can't mix API modes"; + case VORBIS_outofmem: return "VORBIS: out of memory"; + case VORBIS_feature_not_supported: return "VORBIS: feature not supported"; + case VORBIS_too_many_channels: return "VORBIS: too many channels"; + case VORBIS_seek_without_length: return "VORBIS: can't seek in unknown length stream"; + case VORBIS_unexpected_eof: return "VORBIS: unexpected eof"; + case VORBIS_seek_invalid: return "VORBIS: invalid seek"; + case VORBIS_invalid_setup: return "VORBIS: invalid setup"; + case VORBIS_invalid_stream: return "VORBIS: invalid stream"; + case VORBIS_missing_capture_pattern: return "VORBIS: missing capture pattern"; + case VORBIS_invalid_stream_structure_version: return "VORBIS: invalid stream structure version"; + case VORBIS_continued_packet_flag_invalid: return "VORBIS: continued packet flag invalid"; + case VORBIS_incorrect_stream_serial_number: return "VORBIS: incorrect stream serial number"; + case VORBIS_invalid_first_page: return "VORBIS: invalid first page"; + case VORBIS_bad_packet_type: return "VORBIS: bad packet type"; + case VORBIS_cant_find_last_page: return "VORBIS: can't find last page"; + case VORBIS_seek_failed: return "VORBIS: seek failed"; + default: break; + } /* switch */ + + return "VORBIS: unknown error"; +} /* vorbis_error_string */ + +static int VORBIS_init(void) +{ + return 1; /* always succeeds. */ +} /* VORBIS_init */ + +static void VORBIS_quit(void) +{ + /* it's a no-op. */ +} /* VORBIS_quit */ + +static int VORBIS_open(Sound_Sample *sample, const char *ext) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + int err = 0; + stb_vorbis *stb = stb_vorbis_open_rwops(rw, 0, &err, NULL); + unsigned int num_frames; + + BAIL_IF_MACRO(!stb, vorbis_error_string(err), 0); + + SNDDBG(("VORBIS: Accepting data stream.\n")); + + internal->decoder_private = stb; + sample->flags = SOUND_SAMPLEFLAG_CANSEEK; + sample->actual.format = AUDIO_F32SYS; + sample->actual.channels = stb->channels; + sample->actual.rate = stb->sample_rate; + num_frames = stb_vorbis_stream_length_in_samples(stb); + if (!num_frames) + internal->total_time = -1; + else + { + const unsigned int rate = stb->sample_rate; + internal->total_time = (num_frames / rate) * 1000; + internal->total_time += (num_frames % rate) * 1000 / rate; + } /* else */ + + return 1; /* we'll handle this data. */ +} /* VORBIS_open */ + + +static void VORBIS_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; + stb_vorbis_close(stb); +} /* VORBIS_close */ + + +static Uint32 VORBIS_read(Sound_Sample *sample) +{ + Uint32 retval; + int rc; + int err; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; + const int channels = (int) sample->actual.channels; + const int want_samples = (int) (internal->buffer_size / sizeof (float)); + + stb_vorbis_get_error(stb); /* clear any error state */ + rc = stb_vorbis_get_samples_float_interleaved(stb, channels, (float *) internal->buffer, want_samples); + retval = (Uint32) (rc * channels * sizeof (float)); /* rc == number of sample frames read */ + err = stb_vorbis_get_error(stb); + + if (retval == 0) + { + if (!err) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + else + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + /* !!! FIXME: should I set this? __Sound_SetError(vorbis_error_string(err)); */ + } /* else */ + } /* if */ + + else if (retval < internal->buffer_size) + sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + + return retval; +} /* VORBIS_read */ + + +static int VORBIS_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; + BAIL_IF_MACRO(!stb_vorbis_seek_start(stb), vorbis_error_string(stb_vorbis_get_error(stb)), 0); + return 1; +} /* VORBIS_rewind */ + + +static int VORBIS_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + stb_vorbis *stb = (stb_vorbis *) internal->decoder_private; + const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f; + const Uint32 frame_offset = (Uint32) (frames_per_ms * ((float) ms)); + const unsigned int sampnum = (unsigned int) frame_offset; + BAIL_IF_MACRO(!stb_vorbis_seek(stb, sampnum), vorbis_error_string(stb_vorbis_get_error(stb)), 0); + return 1; +} /* VORBIS_seek */ + + +static const char *extensions_vorbis[] = { "OGG", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS = +{ + { + extensions_vorbis, + "Ogg Vorbis audio", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + VORBIS_init, /* init() method */ + VORBIS_quit, /* quit() method */ + VORBIS_open, /* open() method */ + VORBIS_close, /* close() method */ + VORBIS_read, /* read() method */ + VORBIS_rewind, /* rewind() method */ + VORBIS_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_VORBIS */ + +/* end of SDL_sound_vorbis.c ... */ + diff --git a/SDL2_sound/SDL_sound_wav.c b/SDL2_sound/SDL_sound_wav.c new file mode 100644 index 00000000..b5b390d3 --- /dev/null +++ b/SDL2_sound/SDL_sound_wav.c @@ -0,0 +1,783 @@ +/** + * SDL_sound; A sound processing toolkit. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* + * WAV decoder for SDL_sound. + * + * This driver handles Microsoft .WAVs, in as many of the thousands of + * variations as we can. + */ + +#define __SDL_SOUND_INTERNAL__ +#include "SDL_sound_internal.h" + +#if SOUND_SUPPORTS_WAV + +/* Better than SDL_ReadLE16, since you can detect i/o errors... */ +static SDL_INLINE int read_le16(SDL_RWops *rw, Uint16 *ui16) +{ + int rc = SDL_RWread(rw, ui16, sizeof (Uint16), 1); + BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); + *ui16 = SDL_SwapLE16(*ui16); + return 1; +} /* read_le16 */ + + +/* Better than SDL_ReadLE32, since you can detect i/o errors... */ +static SDL_INLINE int read_le32(SDL_RWops *rw, Uint32 *ui32) +{ + int rc = SDL_RWread(rw, ui32, sizeof (Uint32), 1); + BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); + *ui32 = SDL_SwapLE32(*ui32); + return 1; +} /* read_le32 */ + +static SDL_INLINE int read_le16s(SDL_RWops *rw, Sint16 *si16) +{ + return read_le16(rw, (Uint16 *) si16); +} /* read_le16s */ + +static SDL_INLINE int read_le32s(SDL_RWops *rw, Sint32 *si32) +{ + return read_le32(rw, (Uint32 *) si32); +} /* read_le32s */ + + +/* This is just cleaner on the caller's end... */ +static SDL_INLINE int read_uint8(SDL_RWops *rw, Uint8 *ui8) +{ + int rc = SDL_RWread(rw, ui8, sizeof (Uint8), 1); + BAIL_IF_MACRO(rc != 1, ERR_IO_ERROR, 0); + return 1; +} /* read_uint8 */ + + + /* Chunk management code... */ + +#define riffID 0x46464952 /* "RIFF", in ascii. */ +#define waveID 0x45564157 /* "WAVE", in ascii. */ +#define factID 0x74636166 /* "fact", in ascii. */ + + +/***************************************************************************** + * The FORMAT chunk... * + *****************************************************************************/ + +#define fmtID 0x20746D66 /* "fmt ", in ascii. */ + +#define FMT_NORMAL 0x0001 /* Uncompressed waveform data. */ +#define FMT_ADPCM 0x0002 /* ADPCM compressed waveform data. */ + +typedef struct +{ + Sint16 iCoef1; + Sint16 iCoef2; +} ADPCMCOEFSET; + +typedef struct +{ + Uint8 bPredictor; + Uint16 iDelta; + Sint16 iSamp1; + Sint16 iSamp2; +} ADPCMBLOCKHEADER; + +typedef struct S_WAV_FMT_T +{ + Uint32 chunkID; + Sint32 chunkSize; + Sint16 wFormatTag; + Uint16 wChannels; + Uint32 dwSamplesPerSec; + Uint32 dwAvgBytesPerSec; + Uint16 wBlockAlign; + Uint16 wBitsPerSample; + + Uint32 next_chunk_offset; + + Uint32 sample_frame_size; + Uint32 data_starting_offset; + Uint32 total_bytes; + + void (*free)(struct S_WAV_FMT_T *fmt); + Uint32 (*read_sample)(Sound_Sample *sample); + int (*rewind_sample)(Sound_Sample *sample); + int (*seek_sample)(Sound_Sample *sample, Uint32 ms); + + union + { + struct + { + Uint16 cbSize; + Uint16 wSamplesPerBlock; + Uint16 wNumCoef; + ADPCMCOEFSET *aCoef; + ADPCMBLOCKHEADER *blockheaders; + Uint32 samples_left_in_block; + int nibble_state; + Sint8 nibble; + } adpcm; + + /* put other format-specific data here... */ + } fmt; +} fmt_t; + + +/* + * Read in a fmt_t from disk. This makes this process safe regardless of + * the processor's byte order or how the fmt_t structure is packed. + * Note that the union "fmt" is not read in here; that is handled as + * needed in the read_fmt_* functions. + */ +static int read_fmt_chunk(SDL_RWops *rw, fmt_t *fmt) +{ + /* skip reading the chunk ID, since it was already read at this point... */ + fmt->chunkID = fmtID; + + BAIL_IF_MACRO(!read_le32s(rw, &fmt->chunkSize), NULL, 0); + BAIL_IF_MACRO(fmt->chunkSize < 16, "WAV: Invalid chunk size", 0); + fmt->next_chunk_offset = SDL_RWtell(rw) + fmt->chunkSize; + + BAIL_IF_MACRO(!read_le16s(rw, &fmt->wFormatTag), NULL, 0); + BAIL_IF_MACRO(!read_le16(rw, &fmt->wChannels), NULL, 0); + BAIL_IF_MACRO(!read_le32(rw, &fmt->dwSamplesPerSec), NULL, 0); + BAIL_IF_MACRO(!read_le32(rw, &fmt->dwAvgBytesPerSec), NULL, 0); + BAIL_IF_MACRO(!read_le16(rw, &fmt->wBlockAlign), NULL, 0); + BAIL_IF_MACRO(!read_le16(rw, &fmt->wBitsPerSample), NULL, 0); + + return 1; +} /* read_fmt_chunk */ + + + +/***************************************************************************** + * The DATA chunk... * + *****************************************************************************/ + +#define dataID 0x61746164 /* "data", in ascii. */ + +typedef struct +{ + Uint32 chunkID; + Sint32 chunkSize; + /* Then, (chunkSize) bytes of waveform data... */ +} data_t; + + +/* + * Read in a data_t from disk. This makes this process safe regardless of + * the processor's byte order or how the fmt_t structure is packed. + */ +static int read_data_chunk(SDL_RWops *rw, data_t *data) +{ + /* skip reading the chunk ID, since it was already read at this point... */ + data->chunkID = dataID; + BAIL_IF_MACRO(!read_le32s(rw, &data->chunkSize), NULL, 0); + return 1; +} /* read_data_chunk */ + + + + +/***************************************************************************** + * this is what we store in our internal->decoder_private field... * + *****************************************************************************/ + +typedef struct +{ + fmt_t *fmt; + Sint32 bytesLeft; +} wav_t; + + + + +/***************************************************************************** + * Normal, uncompressed waveform handler... * + *****************************************************************************/ + +/* + * Sound_Decode() lands here for uncompressed WAVs... + */ +static Uint32 read_sample_fmt_normal(Sound_Sample *sample) +{ + Uint32 retval; + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + Uint32 max = (internal->buffer_size < (Uint32) w->bytesLeft) ? + internal->buffer_size : (Uint32) w->bytesLeft; + + SDL_assert(max > 0); + + /* + * We don't actually do any decoding, so we read the wav data + * directly into the internal buffer... + */ + retval = SDL_RWread(internal->rw, internal->buffer, 1, max); + + w->bytesLeft -= retval; + + /* Make sure the read went smoothly... */ + if ((retval == 0) || (w->bytesLeft == 0)) + sample->flags |= SOUND_SAMPLEFLAG_EOF; + + else if (retval == -1) + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + + /* (next call this EAGAIN may turn into an EOF or error.) */ + else if (retval < internal->buffer_size) + sample->flags |= SOUND_SAMPLEFLAG_EAGAIN; + + return retval; +} /* read_sample_fmt_normal */ + + +static int seek_sample_fmt_normal(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + int offset = __Sound_convertMsToBytePos(&sample->actual, ms); + int pos = (int) (fmt->data_starting_offset + offset); + int rc = SDL_RWseek(internal->rw, pos, SEEK_SET); + BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0); + w->bytesLeft = fmt->total_bytes - offset; + return 1; /* success. */ +} /* seek_sample_fmt_normal */ + + +static int rewind_sample_fmt_normal(Sound_Sample *sample) +{ + /* no-op. */ + return 1; +} /* rewind_sample_fmt_normal */ + + +static int read_fmt_normal(SDL_RWops *rw, fmt_t *fmt) +{ + /* (don't need to read more from the RWops...) */ + fmt->free = NULL; + fmt->read_sample = read_sample_fmt_normal; + fmt->rewind_sample = rewind_sample_fmt_normal; + fmt->seek_sample = seek_sample_fmt_normal; + return 1; +} /* read_fmt_normal */ + + + +/***************************************************************************** + * ADPCM compression handler... * + *****************************************************************************/ + +#define FIXED_POINT_COEF_BASE 256 +#define FIXED_POINT_ADAPTION_BASE 256 +#define SMALLEST_ADPCM_DELTA 16 + + +static SDL_INLINE int read_adpcm_block_headers(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; + int i; + int max = fmt->wChannels; + + if (w->bytesLeft < fmt->wBlockAlign) + { + sample->flags |= SOUND_SAMPLEFLAG_EOF; + return 0; + } /* if */ + + w->bytesLeft -= fmt->wBlockAlign; + + for (i = 0; i < max; i++) + BAIL_IF_MACRO(!read_uint8(rw, &headers[i].bPredictor), NULL, 0); + + for (i = 0; i < max; i++) + BAIL_IF_MACRO(!read_le16(rw, &headers[i].iDelta), NULL, 0); + + for (i = 0; i < max; i++) + BAIL_IF_MACRO(!read_le16s(rw, &headers[i].iSamp1), NULL, 0); + + for (i = 0; i < max; i++) + BAIL_IF_MACRO(!read_le16s(rw, &headers[i].iSamp2), NULL, 0); + + fmt->fmt.adpcm.samples_left_in_block = fmt->fmt.adpcm.wSamplesPerBlock; + fmt->fmt.adpcm.nibble_state = 0; + return 1; +} /* read_adpcm_block_headers */ + + +static SDL_INLINE void do_adpcm_nibble(Uint8 nib, + ADPCMBLOCKHEADER *header, + Sint32 lPredSamp) +{ + static const Sint32 max_audioval = ((1<<(16-1))-1); + static const Sint32 min_audioval = -(1<<(16-1)); + static const Sint32 AdaptionTable[] = + { + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 + }; + + Sint32 lNewSamp; + Sint32 delta; + + if (nib & 0x08) + lNewSamp = lPredSamp + (header->iDelta * (nib - 0x10)); + else + lNewSamp = lPredSamp + (header->iDelta * nib); + + /* clamp value... */ + if (lNewSamp < min_audioval) + lNewSamp = min_audioval; + else if (lNewSamp > max_audioval) + lNewSamp = max_audioval; + + delta = ((Sint32) header->iDelta * AdaptionTable[nib]) / + FIXED_POINT_ADAPTION_BASE; + + if (delta < SMALLEST_ADPCM_DELTA) + delta = SMALLEST_ADPCM_DELTA; + + header->iDelta = delta; + header->iSamp2 = header->iSamp1; + header->iSamp1 = lNewSamp; +} /* do_adpcm_nibble */ + + +static SDL_INLINE int decode_adpcm_sample_frame(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; + SDL_RWops *rw = internal->rw; + int i; + int max = fmt->wChannels; + Sint32 delta; + Uint8 nib = fmt->fmt.adpcm.nibble; + + for (i = 0; i < max; i++) + { + Uint8 byte; + Sint16 iCoef1 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef1; + Sint16 iCoef2 = fmt->fmt.adpcm.aCoef[headers[i].bPredictor].iCoef2; + Sint32 lPredSamp = ((headers[i].iSamp1 * iCoef1) + + (headers[i].iSamp2 * iCoef2)) / + FIXED_POINT_COEF_BASE; + + if (fmt->fmt.adpcm.nibble_state == 0) + { + BAIL_IF_MACRO(!read_uint8(rw, &nib), NULL, 0); + fmt->fmt.adpcm.nibble_state = 1; + do_adpcm_nibble(nib >> 4, &headers[i], lPredSamp); + } /* if */ + else + { + fmt->fmt.adpcm.nibble_state = 0; + do_adpcm_nibble(nib & 0x0F, &headers[i], lPredSamp); + } /* else */ + } /* for */ + + fmt->fmt.adpcm.nibble = nib; + return 1; +} /* decode_adpcm_sample_frame */ + + +static SDL_INLINE void put_adpcm_sample_frame1(void *_buf, fmt_t *fmt) +{ + Uint16 *buf = (Uint16 *) _buf; + ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; + int i; + for (i = 0; i < fmt->wChannels; i++) + *(buf++) = headers[i].iSamp1; +} /* put_adpcm_sample_frame1 */ + + +static SDL_INLINE void put_adpcm_sample_frame2(void *_buf, fmt_t *fmt) +{ + Uint16 *buf = (Uint16 *) _buf; + ADPCMBLOCKHEADER *headers = fmt->fmt.adpcm.blockheaders; + int i; + for (i = 0; i < fmt->wChannels; i++) + *(buf++) = headers[i].iSamp2; +} /* put_adpcm_sample_frame2 */ + + +/* + * Sound_Decode() lands here for ADPCM-encoded WAVs... + */ +static Uint32 read_sample_fmt_adpcm(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + Uint32 bw = 0; + + while (bw < internal->buffer_size) + { + /* write ongoing sample frame before reading more data... */ + switch (fmt->fmt.adpcm.samples_left_in_block) + { + case 0: /* need to read a new block... */ + if (!read_adpcm_block_headers(sample)) + { + if ((sample->flags & SOUND_SAMPLEFLAG_EOF) == 0) + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return bw; + } /* if */ + + /* only write first sample frame for now. */ + put_adpcm_sample_frame2((Uint8 *) internal->buffer + bw, fmt); + fmt->fmt.adpcm.samples_left_in_block--; + bw += fmt->sample_frame_size; + break; + + case 1: /* output last sample frame of block... */ + put_adpcm_sample_frame1((Uint8 *) internal->buffer + bw, fmt); + fmt->fmt.adpcm.samples_left_in_block--; + bw += fmt->sample_frame_size; + break; + + default: /* output latest sample frame and read a new one... */ + put_adpcm_sample_frame1((Uint8 *) internal->buffer + bw, fmt); + fmt->fmt.adpcm.samples_left_in_block--; + bw += fmt->sample_frame_size; + + if (!decode_adpcm_sample_frame(sample)) + { + sample->flags |= SOUND_SAMPLEFLAG_ERROR; + return bw; + } /* if */ + } /* switch */ + } /* while */ + + return bw; +} /* read_sample_fmt_adpcm */ + + +/* + * Sound_FreeSample() lands here for ADPCM-encoded WAVs... + */ +static void free_fmt_adpcm(fmt_t *fmt) +{ + if (fmt->fmt.adpcm.aCoef != NULL) + SDL_free(fmt->fmt.adpcm.aCoef); + + if (fmt->fmt.adpcm.blockheaders != NULL) + SDL_free(fmt->fmt.adpcm.blockheaders); +} /* free_fmt_adpcm */ + + +static int rewind_sample_fmt_adpcm(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + w->fmt->fmt.adpcm.samples_left_in_block = 0; + return 1; +} /* rewind_sample_fmt_adpcm */ + + +static int seek_sample_fmt_adpcm(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + Uint32 origsampsleft = fmt->fmt.adpcm.samples_left_in_block; + int origpos = SDL_RWtell(internal->rw); + int offset = __Sound_convertMsToBytePos(&sample->actual, ms); + int bpb = (fmt->fmt.adpcm.wSamplesPerBlock * fmt->sample_frame_size); + int skipsize = (offset / bpb) * fmt->wBlockAlign; + int pos = skipsize + fmt->data_starting_offset; + int rc = SDL_RWseek(internal->rw, pos, SEEK_SET); + BAIL_IF_MACRO(rc != pos, ERR_IO_ERROR, 0); + + /* The offset we need is in this block, so we need to decode to there. */ + skipsize += (offset % bpb); + rc = (offset % bpb); /* bytes into this block we need to decode */ + if (!read_adpcm_block_headers(sample)) + { + SDL_RWseek(internal->rw, origpos, SEEK_SET); /* try to make sane. */ + return 0; + } /* if */ + + /* first sample frame of block is a freebie. :) */ + fmt->fmt.adpcm.samples_left_in_block--; + rc -= fmt->sample_frame_size; + while (rc > 0) + { + if (!decode_adpcm_sample_frame(sample)) + { + SDL_RWseek(internal->rw, origpos, SEEK_SET); + fmt->fmt.adpcm.samples_left_in_block = origsampsleft; + return 0; + } /* if */ + + fmt->fmt.adpcm.samples_left_in_block--; + rc -= fmt->sample_frame_size; + } /* while */ + + w->bytesLeft = fmt->total_bytes - skipsize; + return 1; /* success. */ +} /* seek_sample_fmt_adpcm */ + + +/* + * Read in the adpcm-specific info from disk. This makes this process + * safe regardless of the processor's byte order or how the fmt_t + * structure is packed. + */ +static int read_fmt_adpcm(SDL_RWops *rw, fmt_t *fmt) +{ + size_t i; + + SDL_memset(&fmt->fmt.adpcm, '\0', sizeof (fmt->fmt.adpcm)); + fmt->free = free_fmt_adpcm; + fmt->read_sample = read_sample_fmt_adpcm; + fmt->rewind_sample = rewind_sample_fmt_adpcm; + fmt->seek_sample = seek_sample_fmt_adpcm; + + BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.cbSize), NULL, 0); + BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wSamplesPerBlock), NULL, 0); + BAIL_IF_MACRO(!read_le16(rw, &fmt->fmt.adpcm.wNumCoef), NULL, 0); + + /* fmt->free() is always called, so these malloc()s will be cleaned up. */ + + i = sizeof (ADPCMCOEFSET) * fmt->fmt.adpcm.wNumCoef; + fmt->fmt.adpcm.aCoef = (ADPCMCOEFSET *) SDL_malloc(i); + BAIL_IF_MACRO(fmt->fmt.adpcm.aCoef == NULL, ERR_OUT_OF_MEMORY, 0); + + for (i = 0; i < fmt->fmt.adpcm.wNumCoef; i++) + { + BAIL_IF_MACRO(!read_le16s(rw, &fmt->fmt.adpcm.aCoef[i].iCoef1), NULL, 0); + BAIL_IF_MACRO(!read_le16s(rw, &fmt->fmt.adpcm.aCoef[i].iCoef2), NULL, 0); + } /* for */ + + i = sizeof (ADPCMBLOCKHEADER) * fmt->wChannels; + fmt->fmt.adpcm.blockheaders = (ADPCMBLOCKHEADER *) SDL_malloc(i); + BAIL_IF_MACRO(fmt->fmt.adpcm.blockheaders == NULL, ERR_OUT_OF_MEMORY, 0); + + return 1; +} /* read_fmt_adpcm */ + + + +/***************************************************************************** + * Everything else... * + *****************************************************************************/ + +static int WAV_init(void) +{ + return 1; /* always succeeds. */ +} /* WAV_init */ + + +static void WAV_quit(void) +{ + /* it's a no-op. */ +} /* WAV_quit */ + + +static int read_fmt(SDL_RWops *rw, fmt_t *fmt) +{ + /* if it's in this switch statement, we support the format. */ + switch (fmt->wFormatTag) + { + case FMT_NORMAL: + SNDDBG(("WAV: Appears to be uncompressed audio.\n")); + return read_fmt_normal(rw, fmt); + + case FMT_ADPCM: + SNDDBG(("WAV: Appears to be ADPCM compressed audio.\n")); + return read_fmt_adpcm(rw, fmt); + + /* add other types here. */ + + default: + SNDDBG(("WAV: Format 0x%X is unknown.\n", + (unsigned int) fmt->wFormatTag)); + BAIL_MACRO("WAV: Unsupported format", 0); + } /* switch */ + + SDL_assert(0); /* shouldn't hit this point. */ + return 0; +} /* read_fmt */ + + +/* + * Locate a specific chunk in the WAVE file by ID... + */ +static int find_chunk(SDL_RWops *rw, Uint32 id) +{ + Sint32 siz = 0; + Uint32 _id = 0; + Uint32 pos = SDL_RWtell(rw); + + while (1) + { + BAIL_IF_MACRO(!read_le32(rw, &_id), NULL, 0); + if (_id == id) + return 1; + + /* skip ahead and see what next chunk is... */ + BAIL_IF_MACRO(!read_le32s(rw, &siz), NULL, 0); + SDL_assert(siz >= 0); + pos += (sizeof (Uint32) * 2) + siz; + if (siz > 0) + BAIL_IF_MACRO(SDL_RWseek(rw, pos, SEEK_SET) != pos, NULL, 0); + } /* while */ + + return 0; /* shouldn't hit this, but just in case... */ +} /* find_chunk */ + + +static int WAV_open_internal(Sound_Sample *sample, const char *ext, fmt_t *fmt) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + SDL_RWops *rw = internal->rw; + data_t d; + wav_t *w; + Uint32 pos; + + BAIL_IF_MACRO(SDL_ReadLE32(rw) != riffID, "WAV: Not a RIFF file.", 0); + SDL_ReadLE32(rw); /* throw the length away; we get this info later. */ + BAIL_IF_MACRO(SDL_ReadLE32(rw) != waveID, "WAV: Not a WAVE file.", 0); + BAIL_IF_MACRO(!find_chunk(rw, fmtID), "WAV: No format chunk.", 0); + BAIL_IF_MACRO(!read_fmt_chunk(rw, fmt), "WAV: Can't read format chunk.", 0); + + /* !!! FIXME: need float32 format stuff, since it's not just wBitsPerSample. */ + + sample->actual.channels = (Uint8) fmt->wChannels; + sample->actual.rate = fmt->dwSamplesPerSec; + if (fmt->wBitsPerSample == 4) + sample->actual.format = AUDIO_S16SYS; + else if (fmt->wBitsPerSample == 8) + sample->actual.format = AUDIO_U8; + else if (fmt->wBitsPerSample == 16) + sample->actual.format = AUDIO_S16LSB; + else if (fmt->wBitsPerSample == 32) + sample->actual.format = AUDIO_S32LSB; + else + { + SNDDBG(("WAV: %d bits per sample!?\n", (int) fmt->wBitsPerSample)); + BAIL_MACRO("WAV: Unsupported sample size.", 0); + } /* else */ + + BAIL_IF_MACRO(!read_fmt(rw, fmt), NULL, 0); + SDL_RWseek(rw, fmt->next_chunk_offset, SEEK_SET); + BAIL_IF_MACRO(!find_chunk(rw, dataID), "WAV: No data chunk.", 0); + BAIL_IF_MACRO(!read_data_chunk(rw, &d), "WAV: Can't read data chunk.", 0); + + w = (wav_t *) SDL_malloc(sizeof(wav_t)); + BAIL_IF_MACRO(w == NULL, ERR_OUT_OF_MEMORY, 0); + w->fmt = fmt; + fmt->total_bytes = w->bytesLeft = d.chunkSize; + fmt->data_starting_offset = SDL_RWtell(rw); + fmt->sample_frame_size = ( ((sample->actual.format & 0xFF) / 8) * + sample->actual.channels ); + internal->decoder_private = (void *) w; + + internal->total_time = (fmt->total_bytes / fmt->dwAvgBytesPerSec) * 1000; + internal->total_time += (fmt->total_bytes % fmt->dwAvgBytesPerSec) + * 1000 / fmt->dwAvgBytesPerSec; + + sample->flags = SOUND_SAMPLEFLAG_NONE; + if (fmt->seek_sample != NULL) + sample->flags |= SOUND_SAMPLEFLAG_CANSEEK; + + SNDDBG(("WAV: Accepting data stream.\n")); + return 1; /* we'll handle this data. */ +} /* WAV_open_internal */ + + +static int WAV_open(Sound_Sample *sample, const char *ext) +{ + int rc; + + fmt_t *fmt = (fmt_t *) SDL_calloc(1, sizeof (fmt_t)); + BAIL_IF_MACRO(fmt == NULL, ERR_OUT_OF_MEMORY, 0); + + rc = WAV_open_internal(sample, ext, fmt); + if (!rc) + { + if (fmt->free != NULL) + fmt->free(fmt); + SDL_free(fmt); + } /* if */ + + return rc; +} /* WAV_open */ + + +static void WAV_close(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + + if (w->fmt->free != NULL) + w->fmt->free(w->fmt); + + SDL_free(w->fmt); + SDL_free(w); +} /* WAV_close */ + + +static Uint32 WAV_read(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + return w->fmt->read_sample(sample); +} /* WAV_read */ + + +static int WAV_rewind(Sound_Sample *sample) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + fmt_t *fmt = w->fmt; + int rc = SDL_RWseek(internal->rw, fmt->data_starting_offset, SEEK_SET); + BAIL_IF_MACRO(rc != fmt->data_starting_offset, ERR_IO_ERROR, 0); + w->bytesLeft = fmt->total_bytes; + return fmt->rewind_sample(sample); +} /* WAV_rewind */ + + +static int WAV_seek(Sound_Sample *sample, Uint32 ms) +{ + Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque; + wav_t *w = (wav_t *) internal->decoder_private; + return w->fmt->seek_sample(sample, ms); +} /* WAV_seek */ + + +static const char *extensions_wav[] = { "WAV", NULL }; +const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV = +{ + { + extensions_wav, + "Microsoft WAVE audio format", + "Ryan C. Gordon ", + "https://icculus.org/SDL_sound/" + }, + + WAV_init, /* init() method */ + WAV_quit, /* quit() method */ + WAV_open, /* open() method */ + WAV_close, /* close() method */ + WAV_read, /* read() method */ + WAV_rewind, /* rewind() method */ + WAV_seek /* seek() method */ +}; + +#endif /* SOUND_SUPPORTS_WAV */ + +/* end of SDL_sound_wav.c ... */ + diff --git a/SDL2_sound/dr_flac.h b/SDL2_sound/dr_flac.h new file mode 100644 index 00000000..86f242e2 --- /dev/null +++ b/SDL2_sound/dr_flac.h @@ -0,0 +1,5906 @@ +// (this code is from https://github.com/mackron/dr_libs/blob/master/dr_flac.h ... ---ryan.) +// FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file. +// dr_flac - v0.9.7 - 2018-07-05 +// +// David Reid - mackron@gmail.com + +// USAGE +// +// dr_flac is a single-file library. To use it, do something like the following in one .c file. +// #define DR_FLAC_IMPLEMENTATION +// #include "dr_flac.h" +// +// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, +// do something like the following: +// +// drflac* pFlac = drflac_open_file("MySong.flac"); +// if (pFlac == NULL) { +// // Failed to open FLAC file +// } +// +// drflac_int32* pSamples = malloc(pFlac->totalSampleCount * sizeof(drflac_int32)); +// drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSamples); +// +// The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of +// channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are +// always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has +// seamless support for Ogg encapsulated FLAC streams as well. +// +// You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and +// the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of +// samples, just call it again. Example: +// +// while (drflac_read_s32(pFlac, chunkSize, pChunkSamples) > 0) { +// do_something(); +// } +// +// You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example, +// if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel. +// The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the +// left channel, etc. +// +// +// If you just want to quickly decode an entire FLAC file in one go you can do something like this: +// +// unsigned int channels; +// unsigned int sampleRate; +// drflac_uint64 totalSampleCount; +// drflac_int32* pSampleData = drflac_open_and_decode_file_s32("MySong.flac", &channels, &sampleRate, &totalSampleCount); +// if (pSampleData == NULL) { +// // Failed to open and decode FLAC file. +// } +// +// ... +// +// drflac_free(pSampleData); +// +// +// You can read samples as signed 16-bit integer and 32-bit floating-point PCM with the *_s16() and *_f32() family of APIs +// respectively, but note that these should be considered lossy. +// +// +// If you need access to metadata (album art, etc.), use drflac_open_with_metadata(), drflac_open_file_with_metdata() or +// drflac_open_memory_with_metadata(). The rationale for keeping these APIs separate is that they're slightly slower than the +// normal versions and also just a little bit harder to use. +// +// dr_flac reports metadata to the application through the use of a callback, and every metadata block is reported before +// drflac_open_with_metdata() returns. +// +// +// The main opening APIs (drflac_open(), etc.) will fail if the header is not present. The presents a problem in certain +// scenarios such as broadcast style streams like internet radio where the header may not be present because the user has +// started playback mid-stream. To handle this, use the relaxed APIs: drflac_open_relaxed() and drflac_open_with_metadata_relaxed(). +// +// It is not recommended to use these APIs for file based streams because a missing header would usually indicate a +// corrupted or perverse file. In addition, these APIs can take a long time to initialize because they may need to spend +// a lot of time finding the first frame. +// +// +// +// OPTIONS +// #define these options before including this file. +// +// #define DR_FLAC_NO_STDIO +// Disable drflac_open_file(). +// +// #define DR_FLAC_NO_OGG +// Disables support for Ogg/FLAC streams. +// +// #define DR_FLAC_NO_WIN32_IO +// In the Win32 build, dr_flac uses the Win32 IO APIs for drflac_open_file() by default. This setting will make it use the +// standard FILE APIs instead. Ignored when DR_FLAC_NO_STDIO is #defined. (The rationale for this configuration is that +// there's a bug in one compiler's Win32 implementation of the FILE APIs which is not present in the Win32 IO APIs.) +// +// #define DR_FLAC_BUFFER_SIZE +// Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls +// back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing +// returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of +// onRead(), or increase it if it's very inefficient. Must be a multiple of 8. +// +// #define DR_FLAC_NO_CRC +// Disables CRC checks. This will offer a performance boost when CRC is unnecessary. +// +// #define DR_FLAC_NO_SIMD +// Disables SIMD optimizations (SSE on x86/x64 architectures). Use this if you are having compatibility issues with your +// compiler. +// +// +// +// QUICK NOTES +// - dr_flac does not currently support changing the sample rate nor channel count mid stream. +// - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as. +// - This has not been tested on big-endian architectures. +// - Rice codes in unencoded binary form (see https://xiph.org/flac/format.html#rice_partition) has not been tested. If anybody +// knows where I can find some test files for this, let me know. +// - dr_flac is not thread-safe, but its APIs can be called from any thread so long as you do your own synchronization. +// - When using Ogg encapsulation, a corrupted metadata block will result in drflac_open_with_metadata() and drflac_open() +// returning inconsistent samples. + +#ifndef dr_flac_h +#define dr_flac_h + +#include + +#ifdef __SDL_SOUND_INTERNAL__ +typedef Sint8 drflac_int8; +typedef Uint8 drflac_uint8; +typedef Sint16 drflac_int16; +typedef Uint16 drflac_uint16; +typedef Sint32 drflac_int32; +typedef Uint32 drflac_uint32; +typedef Sint64 drflac_int64; +typedef Uint64 drflac_uint64; +#elif defined(_MSC_VER) && _MSC_VER < 1600 +typedef signed char drflac_int8; +typedef unsigned char drflac_uint8; +typedef signed short drflac_int16; +typedef unsigned short drflac_uint16; +typedef signed int drflac_int32; +typedef unsigned int drflac_uint32; +typedef signed __int64 drflac_int64; +typedef unsigned __int64 drflac_uint64; +#else +#include +typedef int8_t drflac_int8; +typedef uint8_t drflac_uint8; +typedef int16_t drflac_int16; +typedef uint16_t drflac_uint16; +typedef int32_t drflac_int32; +typedef uint32_t drflac_uint32; +typedef int64_t drflac_int64; +typedef uint64_t drflac_uint64; +#endif +typedef drflac_uint8 drflac_bool8; +typedef drflac_uint32 drflac_bool32; +#define DRFLAC_TRUE 1 +#define DRFLAC_FALSE 0 + +// As data is read from the client it is placed into an internal buffer for fast access. This controls the +// size of that buffer. Larger values means more speed, but also more memory. In my testing there is diminishing +// returns after about 4KB, but you can fiddle with this to suit your own needs. Must be a multiple of 8. +#ifndef DR_FLAC_BUFFER_SIZE +#define DR_FLAC_BUFFER_SIZE 4096 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// Check if we can enable 64-bit optimizations. +#if defined(_WIN64) +#define DRFLAC_64BIT +#endif + +#if defined(__GNUC__) +#if defined(__x86_64__) || defined(__ppc64__) +#define DRFLAC_64BIT +#endif +#endif + +#ifdef DRFLAC_64BIT +typedef drflac_uint64 drflac_cache_t; +#else +typedef drflac_uint32 drflac_cache_t; +#endif + +// The various metadata block types. +#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 + +// The various picture types specified in the PICTURE block. +#define DRFLAC_PICTURE_TYPE_OTHER 0 +#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 +#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 +#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 +#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define DRFLAC_PICTURE_TYPE_MEDIA 6 +#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define DRFLAC_PICTURE_TYPE_ARTIST 8 +#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 +#define DRFLAC_PICTURE_TYPE_BAND 10 +#define DRFLAC_PICTURE_TYPE_COMPOSER 11 +#define DRFLAC_PICTURE_TYPE_LYRICIST 12 +#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 + +typedef enum +{ + drflac_container_native, + drflac_container_ogg, + drflac_container_unknown +} drflac_container; + +typedef enum +{ + drflac_seek_origin_start, + drflac_seek_origin_current +} drflac_seek_origin; + +// Packing is important on this structure because we map this directly to the raw data within the SEEKTABLE metadata block. +#pragma pack(2) +typedef struct +{ + drflac_uint64 firstSample; + drflac_uint64 frameOffset; // The offset from the first byte of the header of the first frame. + drflac_uint16 sampleCount; +} drflac_seekpoint; +#pragma pack() + +typedef struct +{ + drflac_uint16 minBlockSize; + drflac_uint16 maxBlockSize; + drflac_uint32 minFrameSize; + drflac_uint32 maxFrameSize; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalSampleCount; + drflac_uint8 md5[16]; +} drflac_streaminfo; + +typedef struct +{ + // The metadata type. Use this to know how to interpret the data below. + drflac_uint32 type; + + // A pointer to the raw data. This points to a temporary buffer so don't hold on to it. It's best to + // not modify the contents of this buffer. Use the structures below for more meaningful and structured + // information about the metadata. It's possible for this to be null. + const void* pRawData; + + // The size in bytes of the block and the buffer pointed to by pRawData if it's non-NULL. + drflac_uint32 rawDataSize; + + union + { + drflac_streaminfo streaminfo; + + struct + { + int unused; + } padding; + + struct + { + drflac_uint32 id; + const void* pData; + drflac_uint32 dataSize; + } application; + + struct + { + drflac_uint32 seekpointCount; + const drflac_seekpoint* pSeekpoints; + } seektable; + + struct + { + drflac_uint32 vendorLength; + const char* vendor; + drflac_uint32 commentCount; + const char* comments; + } vorbis_comment; + + struct + { + char catalog[128]; + drflac_uint64 leadInSampleCount; + drflac_bool32 isCD; + drflac_uint8 trackCount; + const drflac_uint8* pTrackData; + } cuesheet; + + struct + { + drflac_uint32 type; + drflac_uint32 mimeLength; + const char* mime; + drflac_uint32 descriptionLength; + const char* description; + drflac_uint32 width; + drflac_uint32 height; + drflac_uint32 colorDepth; + drflac_uint32 indexColorCount; + drflac_uint32 pictureDataSize; + const drflac_uint8* pPictureData; + } picture; + } data; +} drflac_metadata; + + +// Callback for when data needs to be read from the client. +// +// pUserData [in] The user data that was passed to drflac_open() and family. +// pBufferOut [out] The output buffer. +// bytesToRead [in] The number of bytes to read. +// +// Returns the number of bytes actually read. +// +// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +// either the entire bytesToRead is filled or you have reached the end of the stream. +typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +// Callback for when data needs to be seeked. +// +// pUserData [in] The user data that was passed to drflac_open() and family. +// offset [in] The number of bytes to move, relative to the origin. Will never be negative. +// origin [in] The origin of the seek - the current position or the start of the stream. +// +// Returns whether or not the seek was successful. +// +// The offset will never be negative. Whether or not it is relative to the beginning or current position is determined +// by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current. +typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); + +// Callback for when a metadata block is read. +// +// pUserData [in] The user data that was passed to drflac_open() and family. +// pMetadata [in] A pointer to a structure containing the data of the metadata block. +// +// Use pMetadata->type to determine which metadata block is being handled and how to read the data. +typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); + + +// Structure for internal use. Only used for decoders opened with drflac_open_memory. +typedef struct +{ + const drflac_uint8* data; + size_t dataSize; + size_t currentReadPos; +} drflac__memory_stream; + +// Structure for internal use. Used for bit streaming. +typedef struct +{ + // The function to call when more data needs to be read. + drflac_read_proc onRead; + + // The function to call when the current read position needs to be moved. + drflac_seek_proc onSeek; + + // The user data to pass around to onRead and onSeek. + void* pUserData; + + + // The number of unaligned bytes in the L2 cache. This will always be 0 until the end of the stream is hit. At the end of the + // stream there will be a number of bytes that don't cleanly fit in an L1 cache line, so we use this variable to know whether + // or not the bistreamer needs to run on a slower path to read those last bytes. This will never be more than sizeof(drflac_cache_t). + size_t unalignedByteCount; + + // The content of the unaligned bytes. + drflac_cache_t unalignedCache; + + // The index of the next valid cache line in the "L2" cache. + drflac_uint32 nextL2Line; + + // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. + drflac_uint32 consumedBits; + + // The cached data which was most recently read from the client. There are two levels of cache. Data flows as such: + // Client -> L2 -> L1. The L2 -> L1 movement is aligned and runs on a fast path in just a few instructions. + drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; + drflac_cache_t cache; + + // CRC-16. This is updated whenever bits are read from the bit stream. Manually set this to 0 to reset the CRC. For FLAC, this + // is reset to 0 at the beginning of each frame. + drflac_uint16 crc16; + drflac_cache_t crc16Cache; // A cache for optimizing CRC calculations. This is filled when when the L1 cache is reloaded. + drflac_uint32 crc16CacheIgnoredBytes; // The number of bytes to ignore when updating the CRC-16 from the CRC-16 cache. +} drflac_bs; + +typedef struct +{ + // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. + drflac_uint8 subframeType; + + // The number of wasted bits per sample as specified by the sub-frame header. + drflac_uint8 wastedBitsPerSample; + + // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. + drflac_uint8 lpcOrder; + + // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because + // an extra bit is required for side channels when interchannel decorrelation is being used. + drflac_uint32 bitsPerSample; + + // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. Note that + // it's a signed 32-bit integer for each value. + drflac_int32* pDecodedSamples; +} drflac_subframe; + +typedef struct +{ + // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will + // always be set to 0. + drflac_uint64 sampleNumber; + + // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. + drflac_uint32 frameNumber; + + // The sample rate of this frame. + drflac_uint32 sampleRate; + + // The number of samples in each sub-frame within this frame. + drflac_uint16 blockSize; + + // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this + // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. + drflac_uint8 channelAssignment; + + // The number of bits per sample within this frame. + drflac_uint8 bitsPerSample; + + // The frame's CRC. + drflac_uint8 crc8; +} drflac_frame_header; + +typedef struct +{ + // The header. + drflac_frame_header header; + + // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples + // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. + drflac_uint32 samplesRemaining; + + // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. + drflac_subframe subframes[8]; +} drflac_frame; + +typedef struct +{ + // The function to call when a metadata block is read. + drflac_meta_proc onMeta; + + // The user data posted to the metadata callback function. + void* pUserDataMD; + + + // The sample rate. Will be set to something like 44100. + drflac_uint32 sampleRate; + + // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. Maximum 8. This is set based on the + // value specified in the STREAMINFO block. + drflac_uint8 channels; + + // The bits per sample. Will be set to something like 16, 24, etc. + drflac_uint8 bitsPerSample; + + // The maximum block size, in samples. This number represents the number of samples in each channel (not combined). + drflac_uint16 maxBlockSize; + + // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels, + // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a + // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio. + drflac_uint64 totalSampleCount; + + + // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. + drflac_container container; + + // The number of seekpoints in the seektable. + drflac_uint32 seekpointCount; + + + // Information about the frame the decoder is currently sitting on. + drflac_frame currentFrame; + + // The index of the sample the decoder is currently sitting on. This is only used for seeking. + drflac_uint64 currentSample; + + // The position of the first frame in the stream. This is only ever used for seeking. + drflac_uint64 firstFramePos; + + + // A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). + drflac__memory_stream memoryStream; + + + // A pointer to the decoded sample data. This is an offset of pExtraData. + drflac_int32* pDecodedSamples; + + // A pointer to the seek table. This is an offset of pExtraData, or NULL if there is no seek table. + drflac_seekpoint* pSeekpoints; + + // Internal use only. Only used with Ogg containers. Points to a drflac_oggbs object. This is an offset of pExtraData. + void* _oggbs; + + // The bit streamer. The raw FLAC data is fed through this object. + drflac_bs bs; + + // Variable length extra data. We attach this to the end of the object so we can avoid unnecessary mallocs. + drflac_uint8 pExtraData[1]; +} drflac; + + +// Opens a FLAC decoder. +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated +// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with +// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. +// +// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory() +// to open the stream from a file or from a block of memory respectively. +// +// The STREAMINFO block must be present for this to succeed. Use drflac_open_relaxed() to open a FLAC stream where +// the header may not be present. +// +// See also: drflac_open_file(), drflac_open_memory(), drflac_open_with_metadata(), drflac_close() +drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData); + +// The same as drflac_open(), except attempts to open the stream even when a header block is not present. +// +// Because the header is not necessarily available, the caller must explicitly define the container (Native or Ogg). Do +// not set this to drflac_container_unknown - that is for internal use only. +// +// Opening in relaxed mode will continue reading data from onRead until it finds a valid frame. If a frame is never +// found it will continue forever. To abort, force your onRead callback to return 0, which dr_flac will use as an +// indicator that the end of the stream was found. +drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData); + +// Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// onMeta [in] The function to call for every metadata block. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a DRFLAC_MALLOC() +// and DRFLAC_FREE() for every metadata block except for STREAMINFO and PADDING blocks. +// +// The caller is notified of the metadata via the onMeta callback. All metadata blocks will be handled before the function +// returns. +// +// The STREAMINFO block must be present for this to succeed. Use drflac_open_with_metadata_relaxed() to open a FLAC +// stream where the header may not be present. +// +// Note that this will behave inconsistently with drflac_open() if the stream is an Ogg encapsulated stream and a metadata +// block is corrupted. This is due to the way the Ogg stream recovers from corrupted pages. When drflac_open_with_metadata() +// is being used, the open routine will try to read the contents of the metadata block, whereas drflac_open() will simply +// seek past it (for the sake of efficiency). This inconsistency can result in different samples being returned depending on +// whether or not the stream is being opened with metadata. +// +// See also: drflac_open_file_with_metadata(), drflac_open_memory_with_metadata(), drflac_open(), drflac_close() +drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData); + +// The same as drflac_open_with_metadata(), except attempts to open the stream even when a header block is not present. +// +// See also: drflac_open_with_metadata(), drflac_open_relaxed() +drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData); + +// Closes the given FLAC decoder. +// +// pFlac [in] The decoder to close. +// +// This will destroy the decoder object. +void drflac_close(drflac* pFlac); + + +// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* pBufferOut); + +// Same as drflac_read_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +// +// Note that this is lossy for streams where the bits per sample is larger than 16. +drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut); + +// Same as drflac_read_s32(), except outputs samples as 32-bit floating-point PCM. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +// +// Note that this should be considered lossy due to the nature of floating point numbers not being able to exactly +// represent every possible number. +drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut); + +// Seeks to the sample at the given index. +// +// pFlac [in] The decoder. +// sampleIndex [in] The index of the sample to seek to. See notes below. +// +// Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise. +// +// The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample +// in the left channel; the sample at index 1 is the first sample on the right channel, and so on. +// +// When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with +// something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels))) +drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex); + + + +#ifndef DR_FLAC_NO_STDIO +// Opens a FLAC decoder from the file at the given path. +// +// filename [in] The path of the file to open, either absolute or relative to the current directory. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the +// number of files a process can have open at any given time, so keep this mind if you have many decoders open at the +// same time. +// +// See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close() +drflac* drflac_open_file(const char* filename); + +// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) +// +// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData); +#endif + +// Opens a FLAC decoder from a pre-allocated block of memory +// +// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +// the lifetime of the decoder. +drflac* drflac_open_memory(const void* data, size_t dataSize); + +// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) +// +// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData); + + + +//// High Level APIs //// + +// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a +// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with DRFLAC_FREE(). +// +// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously +// read samples into a dynamically sized buffer on the heap until no samples are left. +// +// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). +drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_s32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +#ifndef DR_FLAC_NO_STDIO +// Same as drflac_open_and_decode_s32() except opens the decoder from a file. +drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_file_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_file_f32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); +#endif + +// Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory. +drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_memory_s32(), except returns signed 16-bit integer samples. +drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Same as drflac_open_and_decode_memory_s32(), except returns 32-bit floating-point samples. +float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount); + +// Frees memory that was allocated internally by dr_flac. +void drflac_free(void* p); + + +// Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. +typedef struct +{ + drflac_uint32 countRemaining; + const char* pRunningData; +} drflac_vorbis_comment_iterator; + +// Initializes a vorbis comment iterator. This can be used for iterating over the vorbis comments in a VORBIS_COMMENT +// metadata block. +void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments); + +// Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The +// returned string is NOT null terminated. +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); + + + +#ifdef __cplusplus +} +#endif +#endif //dr_flac_h + + +/////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef DR_FLAC_IMPLEMENTATION +#include +#include + +// CPU architecture. +#if defined(__x86_64__) || defined(_M_X64) +#define DRFLAC_X64 +#elif defined(__i386) || defined(_M_IX86) +#define DRFLAC_X86 +#elif defined(__arm__) || defined(_M_ARM) +#define DRFLAC_ARM +#endif + +// Compile-time CPU feature support. +#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) + #if defined(_MSC_VER) && !defined(__clang__) + #if _MSC_VER >= 1400 + #include + static void drflac__cpuid(int info[4], int fid) + { + __cpuid(info, fid); + } + #else + #define DRFLAC_NO_CPUID + #endif + #else + #if defined(__GNUC__) || defined(__clang__) + static void drflac__cpuid(int info[4], int fid) + { + __asm__ __volatile__ ( + "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0) + ); + } + #else + #define DRFLAC_NO_CPUID + #endif + #endif +#else +#define DRFLAC_NO_CPUID +#endif + + +#ifdef __linux__ +#define _BSD_SOURCE +#include +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) +#define DRFLAC_HAS_LZCNT_INTRINSIC +#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) +#define DRFLAC_HAS_LZCNT_INTRINSIC +#elif defined(__clang__) + #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) + #define DRFLAC_HAS_LZCNT_INTRINSIC + #endif +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1300 +#define DRFLAC_HAS_BYTESWAP_INTRINSIC +#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) +#define DRFLAC_HAS_BYTESWAP_INTRINSIC +#elif defined(__clang__) + #if __has_builtin(__builtin_bswap16) && __has_builtin(__builtin_bswap32) && __has_builtin(__builtin_bswap64) + #define DRFLAC_HAS_BYTESWAP_INTRINSIC + #endif +#endif + + +// Standard library stuff. +#ifndef DRFLAC_ASSERT +#include +#define DRFLAC_ASSERT(expression) assert(expression) +#endif +#ifndef DRFLAC_MALLOC +#define DRFLAC_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRFLAC_REALLOC +#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRFLAC_FREE +#define DRFLAC_FREE(p) free((p)) +#endif +#ifndef DRFLAC_COPY_MEMORY +#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRFLAC_ZERO_MEMORY +#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif + +#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 // 64 for AVX-512 in the future. + +#ifdef _MSC_VER +#define DRFLAC_INLINE __forceinline +#else +#ifdef __GNUC__ +#define DRFLAC_INLINE inline __attribute__((always_inline)) +#else +#define DRFLAC_INLINE inline +#endif +#endif + +typedef drflac_int32 drflac_result; +#define DRFLAC_SUCCESS 0 +#define DRFLAC_ERROR -1 // A generic error. +#define DRFLAC_INVALID_ARGS -2 +#define DRFLAC_END_OF_STREAM -128 +#define DRFLAC_CRC_MISMATCH -129 + +#define DRFLAC_SUBFRAME_CONSTANT 0 +#define DRFLAC_SUBFRAME_VERBATIM 1 +#define DRFLAC_SUBFRAME_FIXED 8 +#define DRFLAC_SUBFRAME_LPC 32 +#define DRFLAC_SUBFRAME_RESERVED 255 + +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 + +#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 + + +#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define drflac_assert DRFLAC_ASSERT +#define drflac_copy_memory DRFLAC_COPY_MEMORY +#define drflac_zero_memory DRFLAC_ZERO_MEMORY + + +// CPU caps. +static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; +#ifndef DRFLAC_NO_CPUID +static drflac_bool32 drflac__gIsSSE42Supported = DRFLAC_FALSE; +static void drflac__init_cpu_caps() +{ + int info[4]; + drflac_zero_memory(info, sizeof (info)); + + // LZCNT + drflac__cpuid(info, 0x80000001); + drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; + + // SSE4.2 + drflac__cpuid(info, 1); + drflac__gIsSSE42Supported = (info[2] & (1 << 19)) != 0; +} +#endif + + +//// Endian Management //// +static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian() +{ +#if defined(DRFLAC_X86) || defined(DRFLAC_X64) + return DRFLAC_TRUE; +#else + int n = 1; + return (*(char*)&n) == 1; +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ushort(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap16(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_ulong(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap32(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + +static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) +{ +#ifdef DRFLAC_HAS_BYTESWAP_INTRINSIC + #if defined(_MSC_VER) + return _byteswap_uint64(n); + #elif defined(__GNUC__) || defined(__clang__) + return __builtin_bswap64(n); + #else + #error "This compiler does not support the byte swap intrinsic." + #endif +#else + return ((n & (drflac_uint64)0xFF00000000000000) >> 56) | + ((n & (drflac_uint64)0x00FF000000000000) >> 40) | + ((n & (drflac_uint64)0x0000FF0000000000) >> 24) | + ((n & (drflac_uint64)0x000000FF00000000) >> 8) | + ((n & (drflac_uint64)0x00000000FF000000) << 8) | + ((n & (drflac_uint64)0x0000000000FF0000) << 24) | + ((n & (drflac_uint64)0x000000000000FF00) << 40) | + ((n & (drflac_uint64)0x00000000000000FF) << 56); +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) +{ +#ifdef __linux__ + return be16toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint16(n); + } + + return n; +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) +{ +#ifdef __linux__ + return be32toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +#endif +} + +static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) +{ +#ifdef __linux__ + return be64toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint64(n); + } + + return n; +#endif +} + + +static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) +{ +#ifdef __linux__ + return le32toh(n); +#else + if (!drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +#endif +} + + +static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) +{ + drflac_uint32 result = 0; + result |= (n & 0x7F000000) >> 3; + result |= (n & 0x007F0000) >> 2; + result |= (n & 0x00007F00) >> 1; + result |= (n & 0x0000007F) >> 0; + + return result; +} + + + +// The CRC code below is based on this document: http://zlib.net/crc_v3.txt +static drflac_uint8 drflac__crc8_table[] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +static drflac_uint16 drflac__crc16_table[] = { + 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, + 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, + 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, + 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, + 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, + 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, + 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, + 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, + 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, + 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, + 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, + 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, + 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, + 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, + 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, + 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, + 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, + 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, + 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, + 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, + 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, + 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, + 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, + 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, + 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, + 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, + 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, + 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, + 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, + 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, + 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, + 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 +}; + +static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) +{ + return drflac__crc8_table[crc ^ data]; +} + +static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) +{ + drflac_assert(count <= 32); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc8(crc, 0, 8);") + drflac_uint8 p = 0x07; + for (int i = count-1; i >= 0; --i) { + drflac_uint8 bit = (data & (1 << i)) >> i; + if (crc & 0x80) { + crc = ((crc << 1) | bit) ^ p; + } else { + crc = ((crc << 1) | bit); + } + } + return crc; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) +{ + return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) +{ + switch (byteCount) + { +#ifdef DRFLAC_64BIT + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#endif + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + } + + return crc; +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) +{ + drflac_assert(count <= 64); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else +#if 0 + // REFERENCE (use of this implementation requires an explicit flush by doing "drflac_crc16(crc, 0, 16);") + drflac_uint16 p = 0x8005; + for (int i = count-1; i >= 0; --i) { + drflac_uint16 bit = (data & (1ULL << i)) >> i; + if (r & 0x8000) { + r = ((r << 1) | bit) ^ p; + } else { + r = ((r << 1) | bit); + } + } + + return crc; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +#endif +} + +static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) +{ + drflac_assert(count <= 64); + +#ifdef DR_FLAC_NO_CRC + (void)crc; + (void)data; + (void)count; + return 0; +#else + drflac_uint32 wholeBytes = count >> 3; + drflac_uint32 leftoverBits = count - (wholeBytes*8); + + static drflac_uint64 leftoverDataMaskTable[8] = { + 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F + }; + drflac_uint64 leftoverDataMask = leftoverDataMaskTable[leftoverBits]; + + switch (wholeBytes) { + default: + case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0xFF00000000000000 << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00FF000000000000 << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000FF0000000000 << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000FF00000000 << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000FF000000 << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x0000000000FF0000 << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x000000000000FF00 << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & ((drflac_uint64)0x00000000000000FF << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + } + return crc; +#endif +} + + +static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) +{ +#ifdef DRFLAC_64BIT + return drflac_crc16__64bit(crc, data, count); +#else + return drflac_crc16__32bit(crc, data, count); +#endif +} + + +#ifdef DRFLAC_64BIT +#define drflac__be2host__cache_line drflac__be2host_64 +#else +#define drflac__be2host__cache_line drflac__be2host_32 +#endif + +// BIT READING ATTEMPT #2 +// +// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting +// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache +// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an +// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data +// from onRead() is read into. +#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - ((bs)->consumedBits)) +#ifdef DRFLAC_64BIT +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint64)-1LL) >> (_bitCount))) +#else +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((drflac_uint32)-1) >> (_bitCount))) +#endif +#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), _bitCount) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), _bitCount)) +#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) + + +#ifndef DR_FLAC_NO_CRC +static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) +{ + bs->crc16 = 0; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +} + +static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) +{ + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16CacheIgnoredBytes = 0; +} + +static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) +{ + // We should never be flushing in a situation where we are not aligned on a byte boundary. + drflac_assert((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + + // The bits that were read from the L1 cache need to be accumulated. The number of bytes needing to be accumulated is determined + // by the number of bits that have been consumed. + if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + drflac__update_crc16(bs); + } else { + // We only accumulate the consumed bits. + bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + + // The bits that we just accumulated should never be accumulated again. We need to keep track of how many bytes were accumulated + // so we can handle that later. + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; + } + + return bs->crc16; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) +{ + // Fast path. Try loading straight from L2. + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + // If we get here it means we've run out of data in the L2 cache. We'll need to fetch more from the client, if there's + // any left. + if (bs->unalignedByteCount > 0) { + return DRFLAC_FALSE; // If we have any unaligned bytes it means there's no more aligned bytes left in the client. + } + + size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + + bs->nextL2Line = 0; + if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } + + + // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably + // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer + // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to + // the size of the L1 so we'll need to seek backwards by any misaligned bytes. + size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + + // We need to keep track of any unaligned bytes for later use. + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) { + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + } + + if (alignedL1LineCount > 0) { + size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + for (size_t i = alignedL1LineCount; i > 0; --i) { + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; + } + + bs->nextL2Line = (drflac_uint32)offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DRFLAC_TRUE; + } else { + // If we get into this branch it means we weren't able to load any L1-aligned data. + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); + return DRFLAC_FALSE; + } +} + +static drflac_bool32 drflac__reload_cache(drflac_bs* bs) +{ +#ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); +#endif + + // Fast path. Try just moving the next value in the L2 cache to the L1 cache. + if (drflac__reload_l1_cache_from_l2(bs)) { + bs->cache = drflac__be2host__cache_line(bs->cache); + bs->consumedBits = 0; +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; +#endif + return DRFLAC_TRUE; + } + + // Slow path. + + // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last + // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the + // data from the unaligned cache. + size_t bytesRead = bs->unalignedByteCount; + if (bytesRead == 0) { + return DRFLAC_FALSE; + } + + drflac_assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + + bs->cache = drflac__be2host__cache_line(bs->unalignedCache); + bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. + bs->unalignedByteCount = 0; // <-- At this point the unaligned bytes have been moved into the cache and we thus have no more unaligned bytes. + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache >> bs->consumedBits; + bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; +#endif + return DRFLAC_TRUE; +} + +static void drflac__reset_cache(drflac_bs* bs) +{ + bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); // <-- This clears the L2 cache. + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); // <-- This clears the L1 cache. + bs->cache = 0; + bs->unalignedByteCount = 0; // <-- This clears the trailing unaligned bytes. + bs->unalignedCache = 0; + +#ifndef DR_FLAC_NO_CRC + bs->crc16Cache = 0; + bs->crc16CacheIgnoredBytes = 0; +#endif +} + + +static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) +{ + drflac_assert(bs != NULL); + drflac_assert(pResultOut != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 32); + + if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; + } else { + *pResultOut = (drflac_uint32)bs->cache; + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + } + return DRFLAC_TRUE; + } else { + // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. + drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); + drflac_uint32 bitCountLo = bitCount - bitCountHi; + drflac_uint32 resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + + *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 32); + + drflac_uint32 result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + drflac_uint32 signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResult = (drflac_int32)result; + return DRFLAC_TRUE; +} + +#ifdef DRFLAC_64BIT +static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) +{ + drflac_assert(bitCount <= 64); + drflac_assert(bitCount > 32); + + drflac_uint32 resultHi; + if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { + return DRFLAC_FALSE; + } + + drflac_uint32 resultLo; + if (!drflac__read_uint32(bs, 32, &resultLo)) { + return DRFLAC_FALSE; + } + + *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); + return DRFLAC_TRUE; +} +#endif + +// Function below is unused, but leaving it here in case I need to quickly add it again. +#if 0 +static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) +{ + drflac_assert(bitCount <= 64); + + drflac_uint64 result; + if (!drflac__read_uint64(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + drflac_uint64 signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResultOut = (drflac_int64)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 16); + + drflac_uint32 result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint16)result; + return DRFLAC_TRUE; +} + +#if 0 +static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 16); + + drflac_int32 result; + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int16)result; + return DRFLAC_TRUE; +} +#endif + +static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 8); + + drflac_uint32 result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_uint8)result; + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) +{ + drflac_assert(bs != NULL); + drflac_assert(pResult != NULL); + drflac_assert(bitCount > 0); + drflac_assert(bitCount <= 8); + + drflac_int32 result; + if (!drflac__read_int32(bs, bitCount, &result)) { + return DRFLAC_FALSE; + } + + *pResult = (drflac_int8)result; + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +{ + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (drflac_uint32)bitsToSeek; + bs->cache <<= bitsToSeek; + return DRFLAC_TRUE; + } else { + // It straddles the cached data. This function isn't called too frequently so I'm favouring simplicity here. + bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bs->cache = 0; + + // Simple case. Seek in groups of the same number as bits that fit within a cache line. +#ifdef DRFLAC_64BIT + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint64 bin; + if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#else + while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + drflac_uint32 bin; + if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + } +#endif + + // Whole leftover bytes. + while (bitsToSeek >= 8) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, 8, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek -= 8; + } + + // Leftover bits. + if (bitsToSeek > 0) { + drflac_uint8 bin; + if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { + return DRFLAC_FALSE; + } + bitsToSeek = 0; // <-- Necessary for the assert below. + } + + drflac_assert(bitsToSeek == 0); + return DRFLAC_TRUE; + } +} + + +// This function moves the bit streamer to the first bit after the sync code (bit 15 of the of the frame header). It will also update the CRC-16. +static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) +{ + drflac_assert(bs != NULL); + + // The sync code is always aligned to 8 bits. This is convenient for us because it means we can do byte-aligned movements. The first + // thing to do is align to the next byte. + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + + for (;;) { +#ifndef DR_FLAC_NO_CRC + drflac__reset_crc16(bs); +#endif + + drflac_uint8 hi; + if (!drflac__read_uint8(bs, 8, &hi)) { + return DRFLAC_FALSE; + } + + if (hi == 0xFF) { + drflac_uint8 lo; + if (!drflac__read_uint8(bs, 6, &lo)) { + return DRFLAC_FALSE; + } + + if (lo == 0x3E) { + return DRFLAC_TRUE; + } else { + if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return DRFLAC_FALSE; + } + } + } + } + + // Should never get here. + //return DRFLAC_FALSE; +} + + +#if !defined(DR_FLAC_NO_SIMD) && defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#define DRFLAC_IMPLEMENT_CLZ_LZCNT +#endif +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) +#define DRFLAC_IMPLEMENT_CLZ_MSVC +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) +{ + static drflac_uint32 clz_table_4[] = { + 0, + 4, + 3, 3, + 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + + drflac_uint32 n = clz_table_4[x >> (sizeof(x)*8 - 4)]; + if (n == 0) { +#ifdef DRFLAC_64BIT + if ((x & 0xFFFFFFFF00000000ULL) == 0) { n = 32; x <<= 32; } + if ((x & 0xFFFF000000000000ULL) == 0) { n += 16; x <<= 16; } + if ((x & 0xFF00000000000000ULL) == 0) { n += 8; x <<= 8; } + if ((x & 0xF000000000000000ULL) == 0) { n += 4; x <<= 4; } +#else + if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } + if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } + if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; } +#endif + n += clz_table_4[x >> (sizeof(x)*8 - 4)]; + } + + return n - 1; +} + +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT +static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported() +{ + // If the compiler itself does not support the intrinsic then we'll need to return false. +#ifdef DRFLAC_HAS_LZCNT_INTRINSIC + return drflac__gIsLZCNTSupported; +#else + return DRFLAC_FALSE; +#endif +} + +static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) +{ +#if defined(_MSC_VER) && !defined(__clang__) + #ifdef DRFLAC_64BIT + return (drflac_uint32)__lzcnt64(x); + #else + return (drflac_uint32)__lzcnt(x); + #endif +#else + #if defined(__GNUC__) || defined(__clang__) + #ifdef DRFLAC_64BIT + return (drflac_uint32)__builtin_clzll((unsigned long long)x); + #else + return (drflac_uint32)__builtin_clzl((unsigned long)x); + #endif + #else + // Unsupported compiler. + #error "This compiler does not support the lzcnt intrinsic." + #endif +#endif +} +#endif + +#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC +static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) +{ + drflac_uint32 n; +#ifdef DRFLAC_64BIT + _BitScanReverse64((unsigned long*)&n, x); +#else + _BitScanReverse((unsigned long*)&n, x); +#endif + return sizeof(x)*8 - n - 1; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) +{ + // This function assumes at least one bit is set. Checking for 0 needs to be done at a higher level, outside this function. +#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT + if (drflac__is_lzcnt_supported()) { + return drflac__clz_lzcnt(x); + } else +#endif + { + #ifdef DRFLAC_IMPLEMENT_CLZ_MSVC + return drflac__clz_msvc(x); + #else + return drflac__clz_software(x); + #endif + } +} + + +static inline drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +{ + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); + setBitOffsetPlus1 += 1; + + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; + + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return DRFLAC_TRUE; +} + + + +static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) +{ + drflac_assert(bs != NULL); + drflac_assert(offsetFromStart > 0); + + // Seeking from the start is not quite as trivial as it sounds because the onSeek callback takes a signed 32-bit integer (which + // is intentional because it simplifies the implementation of the onSeek callbacks), however offsetFromStart is unsigned 64-bit. + // To resolve we just need to do an initial seek from the start, and then a series of offset seeks to make up the remainder. + if (offsetFromStart > 0x7FFFFFFF) { + drflac_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + + while (bytesRemaining > 0x7FFFFFFF) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + } + + if (bytesRemaining > 0) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + } + + // The cache should be reset to force a reload of fresh data from the client. + drflac__reset_cache(bs); + return DRFLAC_TRUE; +} + + +static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) +{ + drflac_assert(bs != NULL); + drflac_assert(pNumberOut != NULL); + + drflac_uint8 crc = *pCRCOut; + + unsigned char utf8[7]; + drflac_zero_memory(utf8, sizeof (utf8)); + if (!drflac__read_uint8(bs, 8, utf8)) { + *pNumberOut = 0; + return DRFLAC_END_OF_STREAM; + } + crc = drflac_crc8(crc, utf8[0], 8); + + if ((utf8[0] & 0x80) == 0) { + *pNumberOut = utf8[0]; + *pCRCOut = crc; + return DRFLAC_SUCCESS; + } + + int byteCount = 1; + if ((utf8[0] & 0xE0) == 0xC0) { + byteCount = 2; + } else if ((utf8[0] & 0xF0) == 0xE0) { + byteCount = 3; + } else if ((utf8[0] & 0xF8) == 0xF0) { + byteCount = 4; + } else if ((utf8[0] & 0xFC) == 0xF8) { + byteCount = 5; + } else if ((utf8[0] & 0xFE) == 0xFC) { + byteCount = 6; + } else if ((utf8[0] & 0xFF) == 0xFE) { + byteCount = 7; + } else { + *pNumberOut = 0; + return DRFLAC_CRC_MISMATCH; // Bad UTF-8 encoding. + } + + // Read extra bytes. + drflac_assert(byteCount > 1); + + drflac_uint64 result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + for (int i = 1; i < byteCount; ++i) { + if (!drflac__read_uint8(bs, 8, utf8 + i)) { + *pNumberOut = 0; + return DRFLAC_END_OF_STREAM; + } + crc = drflac_crc8(crc, utf8[i], 8); + + result = (result << 6) | (utf8[i] & 0x3F); + } + + *pNumberOut = result; + *pCRCOut = crc; + return DRFLAC_SUCCESS; +} + + + + +// The next two functions are responsible for calculating the prediction. +// +// When the bits per sample is >16 we need to use 64-bit integer arithmetic because otherwise we'll run out of precision. It's +// safe to assume this will be slower on 32-bit platforms so we use a more optimal solution when the bits per sample is <=16. +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_assert(order <= 32); + + // 32-bit version. + + // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. + drflac_int32 prediction = 0; + + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + + return (drflac_int32)(prediction >> shift); +} + +static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_assert(order <= 32); + + // 64-bit version. + + // This method is faster on the 32-bit build when compiling with VC++. See note below. +#ifndef DRFLAC_64BIT + drflac_int64 prediction; + if (order == 8) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + } + else if (order == 7) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + } + else if (order == 3) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + } + else if (order == 6) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + } + else if (order == 5) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + } + else if (order == 4) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + } + else if (order == 12) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + } + else if (order == 2) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + } + else if (order == 1) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + } + else if (order == 10) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + } + else if (order == 9) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + } + else if (order == 11) + { + prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + } + else + { + prediction = 0; + for (int j = 0; j < (int)order; ++j) { + prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; + } + } +#endif + + // VC++ optimizes this to a single jmp instruction, but only the 64-bit build. The 32-bit build generates less efficient code for some + // reason. The ugly version above is faster so we'll just switch between the two depending on the target platform. +#ifdef DRFLAC_64BIT + drflac_int64 prediction = 0; + + switch (order) + { + case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; + } +#endif + + return (drflac_int32)(prediction >> shift); +} + +#if 0 +// Reference implementation for reading and decoding samples with residual. This is intentionally left unoptimized for the +// sake of readability and should only be used as a reference. +static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(pSamplesOut != NULL); + + for (drflac_uint32 i = 0; i < count; ++i) { + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + decodedRice |= (zeroCounter << riceParam); + if ((decodedRice & 0x01)) { + decodedRice = ~(decodedRice >> 1); + } else { + decodedRice = (decodedRice >> 1); + } + + + if (bitsPerSample > 16) { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} +#endif + +#if 0 +static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_uint32 zeroCounter = 0; + for (;;) { + drflac_uint8 bit; + if (!drflac__read_uint8(bs, 1, &bit)) { + return DRFLAC_FALSE; + } + + if (bit == 0) { + zeroCounter += 1; + } else { + break; + } + } + + drflac_uint32 decodedRice; + if (riceParam > 0) { + if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { + return DRFLAC_FALSE; + } + } else { + decodedRice = 0; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = decodedRice; + return DRFLAC_TRUE; +} +#endif + +static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +{ + drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; + + + drflac_uint32 zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + drflac_uint32 setBitOffsetPlus1 = drflac__clz(bs->cache); + zeroCounter += setBitOffsetPlus1; + setBitOffsetPlus1 += 1; + + + drflac_uint32 riceParamPart; + drflac_uint32 riceLength = setBitOffsetPlus1 + riceParam; + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); + + bs->consumedBits += riceLength; + bs->cache <<= riceLength; + } else { + bs->consumedBits += riceLength; + if (setBitOffsetPlus1 < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + bs->cache <<= setBitOffsetPlus1; + } + + // It straddles the cached data. It will never cover more than the next chunk. We just read the number in two parts and combine them. + drflac_uint32 bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); + drflac_cache_t resultHi = bs->cache & riceParamMask; // <-- This mask is OK because all bits after the first bits are always zero. + + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef DR_FLAC_NO_CRC + drflac__update_crc16(bs); + #endif + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->consumedBits = 0; + #ifndef DR_FLAC_NO_CRC + bs->crc16Cache = bs->cache; + #endif + } else { + // Slow path. We need to fetch more data from the client. + if (!drflac__reload_cache(bs)) { + return DRFLAC_FALSE; + } + } + + riceParamPart = (drflac_uint32)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); + + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + } + + *pZeroCounterOut = zeroCounter; + *pRiceParamPartOut = riceParamPart; + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples_with_residual__rice__simple(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(pSamplesOut != NULL); + + static drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + + drflac_uint32 zeroCountPart0; + drflac_uint32 zeroCountPart1; + drflac_uint32 zeroCountPart2; + drflac_uint32 zeroCountPart3; + drflac_uint32 riceParamPart0; + drflac_uint32 riceParamPart1; + drflac_uint32 riceParamPart2; + drflac_uint32 riceParamPart3; + drflac_uint32 i4 = 0; + drflac_uint32 count4 = count >> 2; + while (i4 < count4) { + // Rice extraction. + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !drflac__read_rice_parts(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return DRFLAC_FALSE; + } + + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart1 |= (zeroCountPart1 << riceParam); + riceParamPart2 |= (zeroCountPart2 << riceParam); + riceParamPart3 |= (zeroCountPart3 << riceParam); + + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; + riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; + riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; + + if (bitsPerSample > 16) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 3); + } else { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 3); + } + + i4 += 1; + pSamplesOut += 4; + } + + drflac_uint32 i = i4 << 2; + while (i < count) { + // Rice extraction. + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return DRFLAC_FALSE; + } + + // Rice reconstruction. + riceParamPart0 |= (zeroCountPart0 << riceParam); + riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; + //riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1); + + // Sample reconstruction. + if (bitsPerSample > 16) { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + 0); + } else { + pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + 0); + } + + i += 1; + pSamplesOut += 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ +#if 0 + return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); +#else + return drflac__decode_samples_with_residual__rice__simple(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut); +#endif +} + +// Reads and seeks past a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. +static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + + for (drflac_uint32 i = 0; i < count; ++i) { + drflac_uint32 zeroCountPart; + drflac_uint32 riceParamPart; + if (!drflac__read_rice_parts(bs, riceParam, &zeroCountPart, &riceParamPart)) { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(count > 0); + drflac_assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32); + drflac_assert(pSamplesOut != NULL); + + for (unsigned int i = 0; i < count; ++i) { + if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return DRFLAC_FALSE; + } + + if (bitsPerSample > 16) { + pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + } + + return DRFLAC_TRUE; +} + + +// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called +// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The +// and parameters are used to determine how many residual values need to be decoded. +static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +{ + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); + drflac_assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? + + drflac_uint8 residualMethod; + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; // Unknown or unsupported residual coding method. + } + + // Ignore the first values. + pDecodedSamples += order; + + + drflac_uint8 partitionOrder; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + // From the FLAC spec: + // The Rice partition order in a Rice-coded residual section must be less than or equal to 8. + if (partitionOrder > 8) { + return DRFLAC_FALSE; + } + + // Validation check. + if ((blockSize / (1 << partitionOrder)) <= order) { + return DRFLAC_FALSE; + } + + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); + for (;;) { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 16) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 32) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } else { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + } + + pDecodedSamples += samplesInPartition; + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + + if (partitionOrder != 0) { + samplesInPartition = blockSize / (1 << partitionOrder); + } + } + + return DRFLAC_TRUE; +} + +// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called +// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The +// and parameters are used to determine how many residual values need to be decoded. +static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) +{ + drflac_assert(bs != NULL); + drflac_assert(blockSize != 0); + + drflac_uint8 residualMethod; + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DRFLAC_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DRFLAC_FALSE; // Unknown or unsupported residual coding method. + } + + drflac_uint8 partitionOrder; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DRFLAC_FALSE; + } + + drflac_uint32 samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + drflac_uint32 partitionsRemaining = (1 << partitionOrder); + for (;;) + { + drflac_uint8 riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 16) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DRFLAC_FALSE; + } + if (riceParam == 32) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return DRFLAC_FALSE; + } + } else { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DRFLAC_FALSE; + } + + if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return DRFLAC_FALSE; + } + } + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) +{ + // Only a single sample needs to be decoded here. + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + // We don't really need to expand this, but it does simplify the process of reading samples. If this becomes a performance issue (unlikely) + // we'll want to look at a more efficient way. + for (drflac_uint32 i = 0; i < blockSize; ++i) { + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_int32* pDecodedSamples) +{ + for (drflac_uint32 i = 0; i < blockSize; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + static const drflac_int32 lpcCoefficientsTable[5][4] = { + {0, 0, 0, 0}, + {1, 0, 0, 0}, + {2, -1, 0, 0}, + {3, -3, 1, 0}, + {4, -6, 4, -1} + }; + + // Warm up samples and coefficients. + for (drflac_uint32 i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +{ + drflac_uint8 i; + + // Warm up samples. + for (i = 0; i < lpcOrder; ++i) { + drflac_int32 sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DRFLAC_FALSE; + } + + pDecodedSamples[i] = sample; + } + + drflac_uint8 lpcPrecision; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; // Invalid. + } + lpcPrecision += 1; + + + drflac_int8 lpcShift; + if (!drflac__read_int8(bs, 5, &lpcShift)) { + return DRFLAC_FALSE; + } + + + drflac_int32 coefficients[32]; + for (i = 0; i < lpcOrder; ++i) { + if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { + return DRFLAC_FALSE; + } + } + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) { + return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static drflac_bool32 drflac__read_next_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) +{ + drflac_assert(bs != NULL); + drflac_assert(header != NULL); + + const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; // -1 = reserved. + + // Keep looping until we find a valid sync code. + for (;;) { + if (!drflac__find_and_seek_to_next_sync_code(bs)) { + return DRFLAC_FALSE; + } + + drflac_uint8 crc8 = 0xCE; // 0xCE = drflac_crc8(0, 0x3FFE, 14); + + drflac_uint8 reserved = 0; + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + + drflac_uint8 blockingStrategy = 0; + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockingStrategy, 1); + + + drflac_uint8 blockSize = 0; + if (!drflac__read_uint8(bs, 4, &blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, blockSize, 4); + + + drflac_uint8 sampleRate = 0; + if (!drflac__read_uint8(bs, 4, &sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, sampleRate, 4); + + + drflac_uint8 channelAssignment = 0; + if (!drflac__read_uint8(bs, 4, &channelAssignment)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, channelAssignment, 4); + + + drflac_uint8 bitsPerSample = 0; + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, bitsPerSample, 3); + + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, reserved, 1); + + + drflac_bool32 isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + drflac_uint64 sampleNumber; + drflac_result result = drflac__read_utf8_coded_number(bs, &sampleNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_END_OF_STREAM) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->frameNumber = 0; + header->sampleNumber = sampleNumber; + } else { + drflac_uint64 frameNumber = 0; + drflac_result result = drflac__read_utf8_coded_number(bs, &frameNumber, &crc8); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_END_OF_STREAM) { + return DRFLAC_FALSE; + } else { + continue; + } + } + header->frameNumber = (drflac_uint32)frameNumber; // <-- Safe cast. + header->sampleNumber = 0; + } + + + if (blockSize == 1) { + header->blockSize = 192; + } else if (blockSize >= 2 && blockSize <= 5) { + header->blockSize = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!drflac__read_uint16(bs, 8, &header->blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSize, 8); + header->blockSize += 1; + } else if (blockSize == 7) { + if (!drflac__read_uint16(bs, 16, &header->blockSize)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->blockSize, 16); + header->blockSize += 1; + } else { + header->blockSize = 256 * (1 << (blockSize - 8)); + } + + + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 8); + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + } else if (sampleRate == 14) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DRFLAC_FALSE; + } + crc8 = drflac_crc8(crc8, header->sampleRate, 16); + header->sampleRate *= 10; + } else { + continue; // Invalid. Assume an invalid block. + } + + + header->channelAssignment = channelAssignment; + + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + + if (!drflac__read_uint8(bs, 8, &header->crc8)) { + return DRFLAC_FALSE; + } + + #ifndef DR_FLAC_NO_CRC + if (header->crc8 != crc8) { + continue; // CRC mismatch. Loop back to the top and find the next sync code. + } + #endif + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +{ + drflac_uint8 header; + if (!drflac__read_uint8(bs, 8, &header)) { + return DRFLAC_FALSE; + } + + // First bit should always be 0. + if ((header & 0x80) != 0) { + return DRFLAC_FALSE; + } + + int type = (header & 0x7E) >> 1; + if (type == 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + } else if (type == 1) { + pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + } else { + if ((type & 0x20) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (type & 0x07); + if (pSubframe->lpcOrder > 4) { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + } + } + + if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { + return DRFLAC_FALSE; + } + + // Wasted bits per sample. + pSubframe->wastedBitsPerSample = 0; + if ((header & 0x01) == 1) { + unsigned int wastedBitsPerSample; + if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return DRFLAC_FALSE; + } + pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) +{ + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); + + drflac_subframe* pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + // Side channels require an extra bit per sample. Took a while to figure that one out... + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } + + // Need to handle wasted bits per sample. + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = pDecodedSamplesOut; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + drflac__decode_samples__constant(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + drflac__decode_samples__verbatim(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + drflac__decode_samples__fixed(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_LPC: + { + drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + +static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +{ + drflac_assert(bs != NULL); + drflac_assert(frame != NULL); + + drflac_subframe* pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DRFLAC_FALSE; + } + + // Side channels require an extra bit per sample. Took a while to figure that one out... + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } + + // Need to handle wasted bits per sample. + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = NULL; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_LPC: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + unsigned char lpcPrecision; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DRFLAC_FALSE; + } + if (lpcPrecision == 15) { + return DRFLAC_FALSE; // Invalid. + } + lpcPrecision += 1; + + + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift. + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DRFLAC_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + return DRFLAC_FALSE; + } + } break; + + default: return DRFLAC_FALSE; + } + + return DRFLAC_TRUE; +} + + +static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) +{ + drflac_assert(channelAssignment <= 10); + + drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + return lookup[channelAssignment]; +} + +static drflac_result drflac__decode_frame(drflac* pFlac) +{ + // This function should be called while the stream is sitting on the first byte after the frame header. + drflac_zero_memory(pFlac->currentFrame.subframes, sizeof(pFlac->currentFrame.subframes)); + + // The frame block size must never be larger than the maximum block size defined by the FLAC stream. + if (pFlac->currentFrame.header.blockSize > pFlac->maxBlockSize) { + return DRFLAC_ERROR; + } + + // The number of channels in the frame must match the channel count from the STREAMINFO block. + int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + if (channelCount != (int)pFlac->channels) { + return DRFLAC_ERROR; + } + + for (int i = 0; i < channelCount; ++i) { + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) { + return DRFLAC_ERROR; + } + } + + drflac_uint8 paddingSizeInBits = DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7; + if (paddingSizeInBits > 0) { + drflac_uint8 padding = 0; + if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return DRFLAC_END_OF_STREAM; + } + } + +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + drflac_uint16 desiredCRC16; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_END_OF_STREAM; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; // CRC mismatch. + } +#endif + + pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount; + + return DRFLAC_SUCCESS; +} + +static drflac_result drflac__seek_frame(drflac* pFlac) +{ + int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + for (int i = 0; i < channelCount; ++i) { + if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) { + return DRFLAC_ERROR; + } + } + + // Padding. + if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return DRFLAC_ERROR; + } + + // CRC. +#ifndef DR_FLAC_NO_CRC + drflac_uint16 actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#endif + drflac_uint16 desiredCRC16; + if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return DRFLAC_END_OF_STREAM; + } + +#ifndef DR_FLAC_NO_CRC + if (actualCRC16 != desiredCRC16) { + return DRFLAC_CRC_MISMATCH; // CRC mismatch. + } +#endif + + return DRFLAC_SUCCESS; +} + +static drflac_bool32 drflac__read_and_decode_next_frame(drflac* pFlac) +{ + drflac_assert(pFlac != NULL); + + for (;;) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + + drflac_result result = drflac__decode_frame(pFlac); + if (result != DRFLAC_SUCCESS) { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Skip to the next frame. + } else { + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; + } +} + + +static void drflac__get_current_frame_sample_range(drflac* pFlac, drflac_uint64* pFirstSampleInFrameOut, drflac_uint64* pLastSampleInFrameOut) +{ + drflac_assert(pFlac != NULL); + + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + + drflac_uint64 firstSampleInFrame = pFlac->currentFrame.header.sampleNumber; + if (firstSampleInFrame == 0) { + firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount; + } + + drflac_uint64 lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount); + if (lastSampleInFrame > 0) { + lastSampleInFrame -= 1; // Needs to be zero based. + } + + if (pFirstSampleInFrameOut) *pFirstSampleInFrameOut = firstSampleInFrame; + if (pLastSampleInFrameOut) *pLastSampleInFrameOut = lastSampleInFrame; +} + +static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) +{ + drflac_assert(pFlac != NULL); + + drflac_bool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); + + drflac_zero_memory(&pFlac->currentFrame, sizeof(pFlac->currentFrame)); + pFlac->currentSample = 0; + + return result; +} + +static DRFLAC_INLINE drflac_result drflac__seek_to_next_frame(drflac* pFlac) +{ + // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. + drflac_assert(pFlac != NULL); + return drflac__seek_frame(pFlac); +} + +static drflac_bool32 drflac__seek_to_sample__brute_force(drflac* pFlac, drflac_uint64 sampleIndex) +{ + drflac_assert(pFlac != NULL); + + drflac_bool32 isMidFrame = DRFLAC_FALSE; + + // If we are seeking forward we start from the current position. Otherwise we need to start all the way from the start of the file. + drflac_uint64 runningSampleCount; + if (sampleIndex >= pFlac->currentSample) { + // Seeking forward. Need to seek from the current position. + runningSampleCount = pFlac->currentSample; + + // The frame header for the first frame may not yet have been read. We need to do that if necessary. + if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + // Seeking backwards. Need to seek from the start of the file. + runningSampleCount = 0; + + // Move back to the start. + if (!drflac__seek_to_first_frame(pFlac)) { + return DRFLAC_FALSE; + } + + // Decode the first frame in preparation for sample-exact seeking below. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } + + // We need to as quickly as possible find the frame that contains the target sample. To do this, we iterate over each frame and inspect its + // header. If based on the header we can determine that the frame contains the sample, we do a full decode of that frame. + for (;;) { + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // We started seeking mid-frame which means we need to skip the frame decoding part. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header. + runningSampleCount += pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + isMidFrame = DRFLAC_FALSE; + } + } + + next_iteration: + // Grab the next frame in preparation for the next iteration. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } +} + + +static drflac_bool32 drflac__seek_to_sample__seek_table(drflac* pFlac, drflac_uint64 sampleIndex) +{ + drflac_assert(pFlac != NULL); + + if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { + return DRFLAC_FALSE; + } + + + drflac_uint32 iClosestSeekpoint = 0; + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + if (pFlac->pSeekpoints[iSeekpoint].firstSample*pFlac->channels >= sampleIndex) { + break; + } + + iClosestSeekpoint = iSeekpoint; + } + + + drflac_bool32 isMidFrame = DRFLAC_FALSE; + + // At this point we should have found the seekpoint closest to our sample. If we are seeking forward and the closest seekpoint is _before_ the current sample, we + // just seek forward from where we are. Otherwise we start seeking from the seekpoint's first sample. + drflac_uint64 runningSampleCount; + if ((sampleIndex >= pFlac->currentSample) && (pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels <= pFlac->currentSample)) { + // Optimized case. Just seek forward from where we are. + runningSampleCount = pFlac->currentSample; + + // The frame header for the first frame may not yet have been read. We need to do that if necessary. + if (pFlac->currentSample == 0 && pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } else { + isMidFrame = DRFLAC_TRUE; + } + } else { + // Slower case. Seek to the start of the seekpoint and then seek forward from there. + runningSampleCount = pFlac->pSeekpoints[iClosestSeekpoint].firstSample*pFlac->channels; + + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + pFlac->pSeekpoints[iClosestSeekpoint].frameOffset)) { + return DRFLAC_FALSE; + } + + // Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } + + for (;;) { + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, but if it's an invalid frame (a CRC mismatch) we need to pretend + // it never existed and keep iterating. + drflac_uint64 samplesToDecode = sampleIndex - runningSampleCount; + + if (!isMidFrame) { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // We started seeking mid-frame which means we need to skip the frame decoding part. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + if (!isMidFrame) { + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + goto next_iteration; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // We started seeking mid-frame which means we need to seek by reading to the end of the frame instead of with + // drflac__seek_to_next_frame() which only works if the decoder is sitting on the byte just after the frame header. + runningSampleCount += pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + isMidFrame = DRFLAC_FALSE; + } + } + + next_iteration: + // Grab the next frame in preparation for the next iteration. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + } +} + + +#ifndef DR_FLAC_NO_OGG +typedef struct +{ + drflac_uint8 capturePattern[4]; // Should be "OggS" + drflac_uint8 structureVersion; // Always 0. + drflac_uint8 headerType; + drflac_uint64 granulePosition; + drflac_uint32 serialNumber; + drflac_uint32 sequenceNumber; + drflac_uint32 checksum; + drflac_uint8 segmentCount; + drflac_uint8 segmentTable[255]; +} drflac_ogg_page_header; +#endif + +typedef struct +{ + drflac_read_proc onRead; + drflac_seek_proc onSeek; + drflac_meta_proc onMeta; + drflac_container container; + void* pUserData; + void* pUserDataMD; + drflac_uint32 sampleRate; + drflac_uint8 channels; + drflac_uint8 bitsPerSample; + drflac_uint64 totalSampleCount; + drflac_uint16 maxBlockSize; + drflac_uint64 runningFilePos; + drflac_bool32 hasStreamInfoBlock; + drflac_bool32 hasMetadataBlocks; + drflac_bs bs; // <-- A bit streamer is required for loading data during initialization. + drflac_frame_header firstFrameHeader; // <-- The header of the first frame that was read during relaxed initalization. Only set if there is no STREAMINFO block. + +#ifndef DR_FLAC_NO_OGG + drflac_uint32 oggSerial; + drflac_uint64 oggFirstBytePos; + drflac_ogg_page_header oggBosHeader; +#endif +} drflac_init_info; + +static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + blockHeader = drflac__be2host_32(blockHeader); + *isLastBlock = (blockHeader & (0x01 << 31)) >> 31; + *blockType = (blockHeader & (0x7F << 24)) >> 24; + *blockSize = (blockHeader & 0xFFFFFF); +} + +static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +{ + drflac_uint32 blockHeader; + if (onRead(pUserData, &blockHeader, 4) != 4) { + return DRFLAC_FALSE; + } + + drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return DRFLAC_TRUE; +} + +drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +{ + // min/max block size. + drflac_uint32 blockSizes; + if (onRead(pUserData, &blockSizes, 4) != 4) { + return DRFLAC_FALSE; + } + + // min/max frame size. + drflac_uint64 frameSizes = 0; + if (onRead(pUserData, &frameSizes, 6) != 6) { + return DRFLAC_FALSE; + } + + // Sample rate, channels, bits per sample and total sample count. + drflac_uint64 importantProps; + if (onRead(pUserData, &importantProps, 8) != 8) { + return DRFLAC_FALSE; + } + + // MD5 + drflac_uint8 md5[16]; + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + return DRFLAC_FALSE; + } + + blockSizes = drflac__be2host_32(blockSizes); + frameSizes = drflac__be2host_64(frameSizes); + importantProps = drflac__be2host_64(importantProps); + + pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16; + pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF; + pStreamInfo->minFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0xFFFFFF0000000000) >> 40); + pStreamInfo->maxFrameSize = (drflac_uint32)((frameSizes & (drflac_uint64)0x000000FFFFFF0000) >> 16); + pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (drflac_uint64)0xFFFFF00000000000) >> 44); + pStreamInfo->channels = (drflac_uint8 )((importantProps & (drflac_uint64)0x00000E0000000000) >> 41) + 1; + pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (drflac_uint64)0x000001F000000000) >> 36) + 1; + pStreamInfo->totalSampleCount = (importantProps & (drflac_uint64)0x0000000FFFFFFFFF) * pStreamInfo->channels; + drflac_copy_memory(pStreamInfo->md5, md5, sizeof(md5)); + + return DRFLAC_TRUE; +} + +drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeektableSize) +{ + // We want to keep track of the byte position in the stream of the seektable. At the time of calling this function we know that + // we'll be sitting on byte 42. + drflac_uint64 runningFilePos = 42; + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; + + for (;;) { + drflac_uint8 isLastBlock = 0; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + runningFilePos += 4; + + + drflac_metadata metadata; + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + + switch (blockType) + { + case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + { + if (onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); + metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); + onMeta(pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { + seektablePos = runningFilePos; + seektableSize = blockSize; + + if (onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.seektable.seekpointCount = blockSize/sizeof(drflac_seekpoint); + metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + + // Endian swap. + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample); + pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset); + pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); + } + + onMeta(pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.comments = pRunningData; + onMeta(pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + drflac_copy_memory(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(drflac_uint64*)pRunningData); pRunningData += 4; + metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = (const drflac_uint8*)pRunningData; + onMeta(pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + metadata.data.picture.type = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.description = pRunningData; + metadata.data.picture.width = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32(*(drflac_uint32*)pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; + onMeta(pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (onMeta) { + metadata.data.padding.unused = 0; + + // Padding doesn't have anything meaningful in it, so just skip over it, but make sure the caller is aware of it by firing the callback. + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + } else { + onMeta(pUserDataMD, &metadata); + } + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + { + // Invalid chunk. Just skip over this one. + if (onMeta) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; // An error occurred while seeking. Attempt to recover by treating this as the last block which will in turn terminate the loop. + } + } + } break; + + default: + { + // It's an unknown chunk, but not necessarily invalid. There's a chance more metadata blocks might be defined later on, so we + // can at the very least report the chunk to the application and let it look at the raw data. + if (onMeta) { + void* pRawData = DRFLAC_MALLOC(blockSize); + if (pRawData == NULL) { + return DRFLAC_FALSE; + } + + if (onRead(pUserData, pRawData, blockSize) != blockSize) { + DRFLAC_FREE(pRawData); + return DRFLAC_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + onMeta(pUserDataMD, &metadata); + + DRFLAC_FREE(pRawData); + } + } break; + } + + // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. + if (onMeta == NULL && blockSize > 0) { + if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { + isLastBlock = DRFLAC_TRUE; + } + } + + runningFilePos += blockSize; + if (isLastBlock) { + break; + } + } + + *pSeektablePos = seektablePos; + *pSeektableSize = seektableSize; + *pFirstFramePos = runningFilePos; + + return DRFLAC_TRUE; +} + +drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + (void)onSeek; + + // Pre: The bit stream should be sitting just past the 4-byte id header. + + pInit->container = drflac_container_native; + + // The first metadata block should be the STREAMINFO block. + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (!relaxed) { + // We're opening in strict mode and the first block is not the STREAMINFO block. Error. + return DRFLAC_FALSE; + } else { + // Relaxed mode. To open from here we need to just find the first frame and set the sample rate, etc. to whatever is defined + // for that frame. + pInit->hasStreamInfoBlock = DRFLAC_FALSE; + pInit->hasMetadataBlocks = DRFLAC_FALSE; + + if (!drflac__read_next_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return DRFLAC_FALSE; // Couldn't find a frame. + } + + if (pInit->firstFrameHeader.bitsPerSample == 0) { + return DRFLAC_FALSE; // Failed to initialize because the first frame depends on the STREAMINFO block, which does not exist. + } + + pInit->sampleRate = pInit->firstFrameHeader.sampleRate; + pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; + pInit->maxBlockSize = 65535; // <-- See notes here: https://xiph.org/flac/format.html#metadata_block_streaminfo + return DRFLAC_TRUE; + } + } else { + drflac_streaminfo streaminfo; + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return DRFLAC_FALSE; + } + + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation). + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + drflac_copy_memory(&metadata.data.streaminfo, &streaminfo, sizeof (metadata.data.streaminfo)); + onMeta(pUserDataMD, &metadata); + } + + return DRFLAC_TRUE; + } +} + +#ifndef DR_FLAC_NO_OGG +#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 +#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 // CRC-32 of "OggS". + +typedef enum +{ + drflac_ogg_recover_on_crc_mismatch, + drflac_ogg_fail_on_crc_mismatch +} drflac_ogg_crc_mismatch_recovery; + + +static drflac_uint32 drflac__crc32_table[] = { + 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, + 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, + 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, + 0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL, + 0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L, + 0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L, + 0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L, + 0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL, + 0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L, + 0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L, + 0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L, + 0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL, + 0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L, + 0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L, + 0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L, + 0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL, + 0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL, + 0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L, + 0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L, + 0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL, + 0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL, + 0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L, + 0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L, + 0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL, + 0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL, + 0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L, + 0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L, + 0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL, + 0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL, + 0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L, + 0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L, + 0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL, + 0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L, + 0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL, + 0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL, + 0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L, + 0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L, + 0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL, + 0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL, + 0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L, + 0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L, + 0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL, + 0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL, + 0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L, + 0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L, + 0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL, + 0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL, + 0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L, + 0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L, + 0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL, + 0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L, + 0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L, + 0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L, + 0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL, + 0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L, + 0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L, + 0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L, + 0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL, + 0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L, + 0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L, + 0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L, + 0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL, + 0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L, + 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L +}; + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) +{ +#ifndef DR_FLAC_NO_CRC + return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#else + (void)data; + return crc32; +#endif +} + +#if 0 +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) +{ + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); + crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); + return crc32; +} + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) +{ + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); + return crc32; +} +#endif + +static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) +{ + // This can be optimized. + for (drflac_uint32 i = 0; i < dataSize; ++i) { + crc32 = drflac_crc32_byte(crc32, pData[i]); + } + return crc32; +} + + +static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) +{ + return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +{ + return 27 + pHeader->segmentCount; +} + +static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +{ + drflac_uint32 pageBodySize = 0; + for (int i = 0; i < pHeader->segmentCount; ++i) { + pageBodySize += pHeader->segmentTable[i]; + } + + return pageBodySize; +} + +drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + drflac_assert(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); + + drflac_uint8 data[23]; + if (onRead(pUserData, data, 23) != 23) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += 23; + + pHeader->structureVersion = data[0]; + pHeader->headerType = data[1]; + drflac_copy_memory(&pHeader->granulePosition, &data[ 2], 8); + drflac_copy_memory(&pHeader->serialNumber, &data[10], 4); + drflac_copy_memory(&pHeader->sequenceNumber, &data[14], 4); + drflac_copy_memory(&pHeader->checksum, &data[18], 4); + pHeader->segmentCount = data[22]; + + // Calculate the CRC. Note that for the calculation the checksum part of the page needs to be set to 0. + data[18] = 0; + data[19] = 0; + data[20] = 0; + data[21] = 0; + + drflac_uint32 i; + for (i = 0; i < 23; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); + } + + + if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += pHeader->segmentCount; + + for (i = 0; i < pHeader->segmentCount; ++i) { + *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + } + + return DRFLAC_SUCCESS; +} + +drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +{ + *pBytesRead = 0; + + drflac_uint8 id[4]; + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += 4; + + // We need to read byte-by-byte until we find the OggS capture pattern. + for (;;) { + if (drflac_ogg__is_capture_pattern(id)) { + *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + + drflac_result result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == DRFLAC_SUCCESS) { + return DRFLAC_SUCCESS; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; + } else { + return result; + } + } + } else { + // The first 4 bytes did not equal the capture pattern. Read the next byte and try again. + id[0] = id[1]; + id[1] = id[2]; + id[2] = id[3]; + if (onRead(pUserData, &id[3], 1) != 1) { + return DRFLAC_END_OF_STREAM; + } + *pBytesRead += 1; + } + } +} + + +// The main part of the Ogg encapsulation is the conversion from the physical Ogg bitstream to the native FLAC bitstream. It works +// in three general stages: Ogg Physical Bitstream -> Ogg/FLAC Logical Bitstream -> FLAC Native Bitstream. dr_flac is designed +// in such a way that the core sections assume everything is delivered in native format. Therefore, for each encapsulation type +// dr_flac is supporting there needs to be a layer sitting on top of the onRead and onSeek callbacks that ensures the bits read from +// the physical Ogg bitstream are converted and delivered in native FLAC format. +typedef struct +{ + drflac_read_proc onRead; // The original onRead callback from drflac_open() and family. + drflac_seek_proc onSeek; // The original onSeek callback from drflac_open() and family. + void* pUserData; // The user data passed on onRead and onSeek. This is the user data that was passed on drflac_open() and family. + drflac_uint64 currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. + drflac_uint64 firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. + drflac_uint32 serialNumber; // The serial number of the FLAC audio pages. This is determined by the initial header page that was read during initialization. + drflac_ogg_page_header bosPageHeader; // Used for seeking. + drflac_ogg_page_header currentPageHeader; + drflac_uint32 bytesRemainingInPage; + drflac_uint32 pageDataSize; + drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; +} drflac_oggbs; // oggbs = Ogg Bitstream + +static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) +{ + size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); + oggbs->currentBytePos += bytesActuallyRead; + + return bytesActuallyRead; +} + +static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) +{ + if (origin == drflac_seek_origin_start) { + if (offset <= 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return DRFLAC_TRUE; + } else { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos = offset; + + return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + } + } else { + while (offset > 0x7FFFFFFF) { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += 0x7FFFFFFF; + offset -= 0x7FFFFFFF; + } + + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { // <-- Safe cast thanks to the loop above. + return DRFLAC_FALSE; + } + oggbs->currentBytePos += offset; + + return DRFLAC_TRUE; + } +} + +static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) +{ + drflac_ogg_page_header header; + for (;;) { + drflac_uint32 crc32 = 0; + drflac_uint32 bytesRead; + if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + oggbs->currentBytePos += bytesRead; + + drflac_uint32 pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { + continue; // Invalid page size. Assume it's corrupted and just move to the next page. + } + + if (header.serialNumber != oggbs->serialNumber) { + // It's not a FLAC page. Skip it. + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + continue; + } + + + // We need to read the entire page and then do a CRC check on it. If there's a CRC mismatch we need to skip this page. + if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return DRFLAC_FALSE; + } + oggbs->pageDataSize = pageBodySize; + +#ifndef DR_FLAC_NO_CRC + drflac_uint32 actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); + if (actualCRC32 != header.checksum) { + if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { + continue; // CRC mismatch. Skip this page. + } else { + // Even though we are failing on a CRC mismatch, we still want our stream to be in a good state. Therefore we + // go to the next valid page to ensure we're in a good state, but return false to let the caller know that the + // seek did not fully complete. + drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); + return DRFLAC_FALSE; + } + } +#else + (void)recoveryMethod; // <-- Silence a warning. +#endif + + drflac_copy_memory(&oggbs->currentPageHeader, &header, sizeof (oggbs->currentPageHeader)); + oggbs->bytesRemainingInPage = pageBodySize; + return DRFLAC_TRUE; + } +} + +// Function below is unused at the moment, but I might be re-adding it later. +#if 0 +static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) +{ + drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + drflac_uint8 iSeg = 0; + drflac_uint32 iByte = 0; + while (iByte < bytesConsumedInPage) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) { + break; + } else { + iSeg += 1; + iByte += segmentSize; + } + } + + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); + return iSeg; +} + +static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +{ + // The current packet ends when we get to the segment with a lacing value of < 255 which is not at the end of a page. + for (;;) { + drflac_bool32 atEndOfPage = DRFLAC_FALSE; + + drflac_uint8 bytesRemainingInSeg; + drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + + drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (segmentSize < 255) { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + atEndOfPage = DRFLAC_TRUE; + } + + break; + } + + bytesToEndOfPacketOrPage += segmentSize; + } + + // At this point we will have found either the packet or the end of the page. If were at the end of the page we'll + // want to load the next page and keep searching for the end of the packet. + drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); + oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; + + if (atEndOfPage) { + // We're potentially at the next packet, but we need to check the next page first to be sure because the packet may + // straddle pages. + if (!drflac_oggbs__goto_next_page(oggbs)) { + return DRFLAC_FALSE; + } + + // If it's a fresh packet it most likely means we're at the next packet. + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + return DRFLAC_TRUE; + } + } else { + // We're at the next packet. + return DRFLAC_TRUE; + } + } +} + +static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +{ + // The bitstream should be sitting on the first byte just after the header of the frame. + + // What we're actually doing here is seeking to the start of the next packet. + return drflac_oggbs__seek_to_next_packet(oggbs); +} +#endif + +static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_assert(oggbs != NULL); + + drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; + + // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. + size_t bytesRead = 0; + while (bytesRead < bytesToRead) { + size_t bytesRemainingToRead = bytesToRead - bytesRead; + + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + bytesRead += bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; + break; + } + + // If we get here it means some of the requested data is contained in the next pages. + if (oggbs->bytesRemainingInPage > 0) { + drflac_copy_memory(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + bytesRead += oggbs->bytesRemainingInPage; + pRunningBufferOut += oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + drflac_assert(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + break; // Failed to go to the next page. Might have simply hit the end of the stream. + } + } + + return bytesRead; +} + +static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + drflac_assert(oggbs != NULL); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + // Seeking is always forward which makes things a lot simpler. + if (origin == drflac_seek_origin_start) { + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + } + + + drflac_assert(origin == drflac_seek_origin_current); + + int bytesSeeked = 0; + while (bytesSeeked < offset) { + int bytesRemainingToSeek = offset - bytesSeeked; + drflac_assert(bytesRemainingToSeek >= 0); + + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + bytesSeeked += bytesRemainingToSeek; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + + // If we get here it means some of the requested data is contained in the next pages. + if (oggbs->bytesRemainingInPage > 0) { + bytesSeeked += (int)oggbs->bytesRemainingInPage; + oggbs->bytesRemainingInPage = 0; + } + + drflac_assert(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { + // Failed to go to the next page. We either hit the end of the stream or had a CRC mismatch. + return DRFLAC_FALSE; + } + } + + return DRFLAC_TRUE; +} + +drflac_bool32 drflac_ogg__seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + + drflac_uint64 originalBytePos = oggbs->currentBytePos; // For recovery. + + // First seek to the first frame. + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) { + return DRFLAC_FALSE; + } + oggbs->bytesRemainingInPage = 0; + + drflac_uint64 runningGranulePosition = 0; + drflac_uint64 runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier. + for (;;) { + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DRFLAC_FALSE; // Never did find that sample... + } + + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sampleIndex) { + break; // The sample is somewhere in the previous page. + } + + + // At this point we know the sample is not in the previous page. It could possibly be in this page. For simplicity we + // disregard any pages that do not begin a fresh packet. + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { // <-- Is it a fresh page? + if (oggbs->currentPageHeader.segmentTable[0] >= 2) { + drflac_uint8 firstBytesInPage[2]; + firstBytesInPage[0] = oggbs->pageData[0]; + firstBytesInPage[1] = oggbs->pageData[1]; + + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code? + runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels; + } + + continue; + } + } + } + + + // We found the page that that is closest to the sample, so now we need to find it. The first thing to do is seek to the + // start of that page. In the loop above we checked that it was a fresh page which means this page is also the start of + // a new frame. This property means that after we've seeked to the page we can immediately start looping over frames until + // we find the one containing the target sample. + if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { + return DRFLAC_FALSE; + } + if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + return DRFLAC_FALSE; + } + + + // At this point we'll be sitting on the first byte of the frame header of the first frame in the page. We just keep + // looping over these frames until we find the one containing the sample we're after. + drflac_uint64 runningSampleCount = runningGranulePosition; + for (;;) { + // There are two ways to find the sample and seek past irrelevant frames: + // 1) Use the native FLAC decoder. + // 2) Use Ogg's framing system. + // + // Both of these options have their own pros and cons. Using the native FLAC decoder is slower because it needs to + // do a full decode of the frame. Using Ogg's framing system is faster, but more complicated and involves some code + // duplication for the decoding of frame headers. + // + // Another thing to consider is that using the Ogg framing system will perform direct seeking of the physical Ogg + // bitstream. This is important to consider because it means we cannot read data from the drflac_bs object using the + // standard drflac__*() APIs because that will read in extra data for its own internal caching which in turn breaks + // the positioning of the read pointer of the physical Ogg bitstream. Therefore, anything that would normally be read + // using the native FLAC decoding APIs, such as drflac__read_next_frame_header(), need to be re-implemented so as to + // avoid the use of the drflac_bs object. + // + // Considering these issues, I have decided to use the slower native FLAC decoding method for the following reasons: + // 1) Seeking is already partially accelerated using Ogg's paging system in the code block above. + // 2) Seeking in an Ogg encapsulated FLAC stream is probably quite uncommon. + // 3) Simplicity. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DRFLAC_FALSE; + } + + drflac_uint64 firstSampleInFrame = 0; + drflac_uint64 lastSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + + drflac_uint64 sampleCountInThisFrame = (lastSampleInFrame - firstSampleInFrame) + 1; + if (sampleIndex < (runningSampleCount + sampleCountInThisFrame)) { + // The sample should be in this frame. We need to fully decode it, however if it's an invalid frame (a CRC mismatch), we need to pretend + // it never existed and keep iterating. + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + // The frame is valid. We just need to skip over some samples to ensure it's sample-exact. + drflac_uint64 samplesToDecode = (size_t)(sampleIndex - runningSampleCount); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DRFLAC_TRUE; + } + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; // <-- If this fails, something bad has happened (it should never fail). + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } else { + // It's not in this frame. We need to seek past the frame, but check if there was a CRC mismatch. If so, we pretend this + // frame never existed and leave the running sample count untouched. + drflac_result result = drflac__seek_to_next_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + runningSampleCount += sampleCountInThisFrame; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + continue; // CRC mismatch. Pretend this frame never existed. + } else { + return DRFLAC_FALSE; + } + } + } + } +} + + +drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +{ + // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern. + (void)relaxed; + + pInit->container = drflac_container_ogg; + pInit->oggFirstBytePos = 0; + + // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the + // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if + // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. + drflac_ogg_page_header header; + + drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; + drflac_uint32 bytesRead = 0; + if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + + for (;;) { + // Break if we're past the beginning of stream page. + if ((header.headerType & 0x02) == 0) { + return DRFLAC_FALSE; + } + + + // Check if it's a FLAC header. + int pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) { // 51 = the lacing value of the FLAC header packet. + // It could be a FLAC page... + drflac_uint32 bytesRemainingInPage = pageBodySize; + + drflac_uint8 packetType; + if (onRead(pUserData, &packetType, 1) != 1) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 1; + if (packetType == 0x7F) { + // Increasingly more likely to be a FLAC page... + drflac_uint8 sig[4]; + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { + // Almost certainly a FLAC page... + drflac_uint8 mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) { + return DRFLAC_FALSE; + } + + if (mappingVersion[0] != 1) { + return DRFLAC_FALSE; // Only supporting version 1.x of the Ogg mapping. + } + + // The next 2 bytes are the non-audio packets, not including this one. We don't care about this because we're going to + // be handling it in a generic way based on the serial number and packet types. + if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + + // Expecting the native FLAC signature "fLaC". + if (onRead(pUserData, sig, 4) != 4) { + return DRFLAC_FALSE; + } + + if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { + // The remaining data in the page should be the STREAMINFO block. + drflac_uint8 isLastBlock; + drflac_uint8 blockType; + drflac_uint32 blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DRFLAC_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return DRFLAC_FALSE; // Invalid block type. First block must be the STREAMINFO block. + } + + drflac_streaminfo streaminfo; + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + // Success! + pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; + pInit->hasMetadataBlocks = !isLastBlock; + + if (onMeta) { + drflac_metadata metadata; + metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + drflac_copy_memory(&metadata.data.streaminfo, &streaminfo, sizeof (metadata.data.streaminfo)); + onMeta(pUserDataMD, &metadata); + } + + pInit->runningFilePos += pageBodySize; + pInit->oggFirstBytePos = pInit->runningFilePos - 79; // Subtracting 79 will place us right on top of the "OggS" identifier of the FLAC bos page. + pInit->oggSerial = header.serialNumber; + drflac_copy_memory(&pInit->oggBosHeader, &header, sizeof (pInit->oggBosHeader)); + break; + } else { + // Failed to read STREAMINFO block. Aww, so close... + return DRFLAC_FALSE; + } + } else { + // Invalid file. + return DRFLAC_FALSE; + } + } else { + // Not a FLAC header. Skip it. + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + // Not a FLAC header. Seek past the entire page and move on to the next. + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + } else { + if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; + } + } + + pInit->runningFilePos += pageBodySize; + + + // Read the header of the next page. + if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { + return DRFLAC_FALSE; + } + pInit->runningFilePos += bytesRead; + } + + + // If we get here it means we found a FLAC audio stream. We should be sitting on the first byte of the header of the next page. The next + // packets in the FLAC logical stream contain the metadata. The only thing left to do in the initialization phase for Ogg is to create the + // Ogg bistream object. + pInit->hasMetadataBlocks = DRFLAC_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. + return DRFLAC_TRUE; +} +#endif + +drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +{ + if (pInit == NULL || onRead == NULL || onSeek == NULL) { + return DRFLAC_FALSE; + } + + drflac_zero_memory(pInit, sizeof(*pInit)); + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->container = container; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; + + pInit->bs.onRead = onRead; + pInit->bs.onSeek = onSeek; + pInit->bs.pUserData = pUserData; + drflac__reset_cache(&pInit->bs); + + + // If the container is explicitly defined then we can try opening in relaxed mode. + drflac_bool32 relaxed = container != drflac_container_unknown; + + drflac_uint8 id[4]; + + // Skip over any ID3 tags. + for (;;) { + if (onRead(pUserData, id, 4) != 4) { + return DRFLAC_FALSE; // Ran out of data. + } + pInit->runningFilePos += 4; + + if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { + drflac_uint8 header[6]; + if (onRead(pUserData, header, 6) != 6) { + return DRFLAC_FALSE; // Ran out of data. + } + pInit->runningFilePos += 6; + + drflac_uint8 flags = header[1]; + drflac_uint32 headerSize; + drflac_copy_memory(&headerSize, header+2, 4); + headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + if (flags & 0x10) { + headerSize += 10; + } + + if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { + return DRFLAC_FALSE; // Failed to seek past the tag. + } + pInit->runningFilePos += headerSize; + } else { + break; + } + } + + if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + + // If we get here it means we likely don't have a header. Try opening in relaxed mode, if applicable. + if (relaxed) { + if (container == drflac_container_native) { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#ifndef DR_FLAC_NO_OGG + if (container == drflac_container_ogg) { + return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + } +#endif + } + + // Unsupported container. + return DRFLAC_FALSE; +} + +void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit) +{ + drflac_assert(pFlac != NULL); + drflac_assert(pInit != NULL); + + drflac_zero_memory(pFlac, sizeof(*pFlac)); + drflac_copy_memory(&pFlac->bs, &pInit->bs, sizeof (pFlac->bs)); + pFlac->onMeta = pInit->onMeta; + pFlac->pUserDataMD = pInit->pUserDataMD; + pFlac->maxBlockSize = pInit->maxBlockSize; + pFlac->sampleRate = pInit->sampleRate; + pFlac->channels = (drflac_uint8)pInit->channels; + pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; + pFlac->totalSampleCount = pInit->totalSampleCount; + pFlac->container = pInit->container; +} + +drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +{ +#ifndef DRFLAC_NO_CPUID + // CPU support first. + drflac__init_cpu_caps(); +#endif + + drflac_init_info init; + if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + return NULL; + } + + // The size of the allocation for the drflac object needs to be large enough to fit the following: + // 1) The main members of the drflac structure + // 2) A block of memory large enough to store the decoded samples of the largest frame in the stream + // 3) If the container is Ogg, a drflac_oggbs object + // + // The complicated part of the allocation is making sure there's enough room the decoded samples, taking into consideration + // the different SIMD instruction sets. + drflac_uint32 allocationSize = sizeof(drflac); + + // The allocation size for decoded frames depends on the number of 32-bit integers that fit inside the largest SIMD vector + // we are supporting. + drflac_uint32 wholeSIMDVectorCountPerChannel; + if ((init.maxBlockSize % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); + } else { + wholeSIMDVectorCountPerChannel = (init.maxBlockSize / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; + } + + drflac_uint32 decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + + allocationSize += decodedSamplesAllocationSize; + allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; // Allocate extra bytes to ensure we have enough for alignment. + +#ifndef DR_FLAC_NO_OGG + // There's additional data required for Ogg streams. + drflac_uint32 oggbsAllocationSize = 0; + if (init.container == drflac_container_ogg) { + oggbsAllocationSize = sizeof(drflac_oggbs); + allocationSize += oggbsAllocationSize; + } + + drflac_oggbs oggbs; + drflac_zero_memory(&oggbs, sizeof(oggbs)); + if (init.container == drflac_container_ogg) { + oggbs.onRead = onRead; + oggbs.onSeek = onSeek; + oggbs.pUserData = pUserData; + oggbs.currentBytePos = init.oggFirstBytePos; + oggbs.firstBytePos = init.oggFirstBytePos; + oggbs.serialNumber = init.oggSerial; + drflac_copy_memory(&oggbs.bosPageHeader, &init.oggBosHeader, sizeof (oggbs.bosPageHeader)); + oggbs.bytesRemainingInPage = 0; + } +#endif + + // This part is a bit awkward. We need to load the seektable so that it can be referenced in-memory, but I want the drflac object to + // consist of only a single heap allocation. To this, the size of the seek table needs to be known, which we determine when reading + // and decoding the metadata. + drflac_uint64 firstFramePos = 42; // <-- We know we are at byte 42 at this point. + drflac_uint64 seektablePos = 0; + drflac_uint32 seektableSize = 0; + if (init.hasMetadataBlocks) { + drflac_read_proc onReadOverride = onRead; + drflac_seek_proc onSeekOverride = onSeek; + void* pUserDataOverride = pUserData; + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + onReadOverride = drflac__on_read_ogg; + onSeekOverride = drflac__on_seek_ogg; + pUserDataOverride = (void*)&oggbs; + } +#endif + + if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seektableSize)) { + return NULL; + } + + allocationSize += seektableSize; + } + + + drflac* pFlac = (drflac*)DRFLAC_MALLOC(allocationSize); + drflac__init_from_info(pFlac, &init); + pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + seektableSize); + drflac_copy_memory(pInternalOggbs, &oggbs, sizeof (drflac_oggbs)); + + // The Ogg bistream needs to be layered on top of the original bitstream. + pFlac->bs.onRead = drflac__on_read_ogg; + pFlac->bs.onSeek = drflac__on_seek_ogg; + pFlac->bs.pUserData = (void*)pInternalOggbs; + pFlac->_oggbs = (void*)pInternalOggbs; + } +#endif + + pFlac->firstFramePos = firstFramePos; + + // NOTE: Seektables are not currently compatible with Ogg encapsulation (Ogg has its own accelerated seeking system). I may change this later, so I'm leaving this here for now. +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) + { + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + else +#endif + { + // If we have a seektable we need to load it now, making sure we move back to where we were previously. + if (seektablePos != 0) { + pFlac->seekpointCount = seektableSize / sizeof(*pFlac->pSeekpoints); + pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + + // Seek to the seektable, then just read directly into our seektable buffer. + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints, seektableSize) == seektableSize) { + // Endian swap. + for (drflac_uint32 iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { + pFlac->pSeekpoints[iSeekpoint].firstSample = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstSample); + pFlac->pSeekpoints[iSeekpoint].frameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].frameOffset); + pFlac->pSeekpoints[iSeekpoint].sampleCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].sampleCount); + } + } else { + // Failed to read the seektable. Pretend we don't have one. + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + + // We need to seek back to where we were. If this fails it's a critical error. + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFramePos, drflac_seek_origin_start)) { + return NULL; + } + } else { + // Failed to seek to the seektable. Ominous sign, but for now we can just pretend we don't have one. + pFlac->pSeekpoints = NULL; + pFlac->seekpointCount = 0; + } + } + } + + + + // If we get here, but don't have a STREAMINFO block, it means we've opened the stream in relaxed mode and need to decode + // the first frame. + if (!init.hasStreamInfoBlock) { + pFlac->currentFrame.header = init.firstFrameHeader; + do + { + drflac_result result = drflac__decode_frame(pFlac); + if (result == DRFLAC_SUCCESS) { + break; + } else { + if (result == DRFLAC_CRC_MISMATCH) { + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + DRFLAC_FREE(pFlac); + return NULL; + } + continue; + } else { + DRFLAC_FREE(pFlac); + return NULL; + } + } + } while (1); + } + + return pFlac; +} + + + +#ifndef DR_FLAC_NO_STDIO +typedef void* drflac_file; + +#if defined(DR_FLAC_NO_WIN32_IO) || !defined(_WIN32) +#include + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +static drflac_file drflac__open_file_handle(const char* filename) +{ + FILE* pFile; +#ifdef _MSC_VER + if (fopen_s(&pFile, filename, "rb") != 0) { + return NULL; + } +#else + pFile = fopen(filename, "rb"); + if (pFile == NULL) { + return NULL; + } +#endif + + return (drflac_file)pFile; +} + +static void drflac__close_file_handle(drflac_file file) +{ + fclose((FILE*)file); +} +#else +#include + +// This doesn't seem to be defined for VC6. +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac_assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion. + + DWORD bytesRead; + ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL); + + return (size_t)bytesRead; +} + +static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + return SetFilePointer((HANDLE)pUserData, offset, NULL, (origin == drflac_seek_origin_current) ? FILE_CURRENT : FILE_BEGIN) != INVALID_SET_FILE_POINTER; +} + +static drflac_file drflac__open_file_handle(const char* filename) +{ + HANDLE hFile = CreateFileA(filename, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return NULL; + } + + return (drflac_file)hFile; +} + +static void drflac__close_file_handle(drflac_file file) +{ + CloseHandle((HANDLE)file); +} +#endif + + +drflac* drflac_open_file(const char* filename) +{ + drflac_file file = drflac__open_file_handle(filename); + if (file == NULL) { + return NULL; + } + + drflac* pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)file); + if (pFlac == NULL) { + drflac__close_file_handle(file); + return NULL; + } + + return pFlac; +} + +drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData) +{ + drflac_file file = drflac__open_file_handle(filename); + if (file == NULL) { + return NULL; + } + + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)file, pUserData); + if (pFlac == NULL) { + drflac__close_file_handle(file); + return pFlac; + } + + return pFlac; +} +#endif //DR_FLAC_NO_STDIO + +static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + drflac_assert(memoryStream != NULL); + drflac_assert(memoryStream->dataSize >= memoryStream->currentReadPos); + + size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + drflac_copy_memory(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + drflac_assert(memoryStream != NULL); + drflac_assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + drflac_assert(offset <= (drflac_int64)memoryStream->dataSize); + + if (origin == drflac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. + } + } else { + if ((drflac_uint32)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. + } + } + + return DRFLAC_TRUE; +} + +drflac* drflac_open_memory(const void* data, size_t dataSize) +{ + drflac__memory_stream memoryStream; + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + // This is an awful hack... +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + +drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData) +{ + drflac__memory_stream memoryStream; + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + // This is an awful hack... +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + + + +drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData); +} +drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData); +} + +drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData); +} +drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData); +} + +void drflac_close(drflac* pFlac) +{ + if (pFlac == NULL) { + return; + } + +#ifndef DR_FLAC_NO_STDIO + // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() + // was used by looking at the callbacks. + if (pFlac->bs.onRead == drflac__on_read_stdio) { + drflac__close_file_handle((drflac_file)pFlac->bs.pUserData); + } + +#ifndef DR_FLAC_NO_OGG + // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. + if (pFlac->container == drflac_container_ogg) { + drflac_assert(pFlac->bs.onRead == drflac__on_read_ogg); + drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + if (oggbs->onRead == drflac__on_read_stdio) { + drflac__close_file_handle((drflac_file)oggbs->pUserData); + } + } +#endif +#endif + + DRFLAC_FREE(pFlac); +} + +drflac_uint64 drflac__read_s32__misaligned(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) +{ + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + + // We should never be calling this when the number of samples to read is >= the sample count. + drflac_assert(samplesToRead < channelCount); + drflac_assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining); + + + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + drflac_uint64 channelIndex = samplesReadFromFrameSoFar % channelCount; + + drflac_uint64 nextSampleInFrame = samplesReadFromFrameSoFar / channelCount; + + int decodedSample = 0; + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + if (channelIndex == 0) { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } else { + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = left - side; + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + if (channelIndex == 0) { + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = side + right; + } else { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + int mid; + int side; + if (channelIndex == 0) { + mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid + side) >> 1; + } else { + mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid - side) >> 1; + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } break; + } + + + decodedSample <<= ((32 - pFlac->bitsPerSample) + pFlac->currentFrame.subframes[channelIndex].wastedBitsPerSample); + + if (bufferOut) { + *bufferOut++ = decodedSample; + } + + samplesRead += 1; + pFlac->currentFrame.samplesRemaining -= 1; + samplesToRead -= 1; + } + + return samplesRead; +} + +drflac_uint64 drflac__seek_forward_by_samples(drflac* pFlac, drflac_uint64 samplesToRead) +{ + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + if (pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_and_decode_next_frame(pFlac)) { + break; // Couldn't read the next frame, so just break from the loop and return. + } + } else { + if (pFlac->currentFrame.samplesRemaining > samplesToRead) { + samplesRead += samplesToRead; + pFlac->currentFrame.samplesRemaining -= (drflac_uint32)samplesToRead; // <-- Safe cast. Will always be < currentFrame.samplesRemaining < 65536. + samplesToRead = 0; + } else { + samplesRead += pFlac->currentFrame.samplesRemaining; + samplesToRead -= pFlac->currentFrame.samplesRemaining; + pFlac->currentFrame.samplesRemaining = 0; + } + } + } + + pFlac->currentSample += samplesRead; + return samplesRead; +} + +drflac_uint64 drflac_read_s32(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int32* bufferOut) +{ + // Note that is allowed to be null, in which case this will act like a seek. + if (pFlac == NULL || samplesToRead == 0) { + return 0; + } + + if (bufferOut == NULL) { + return drflac__seek_forward_by_samples(pFlac, samplesToRead); + } + + + drflac_uint64 samplesRead = 0; + while (samplesToRead > 0) { + // If we've run out of samples in this frame, go to the next. + if (pFlac->currentFrame.samplesRemaining == 0) { + if (!drflac__read_and_decode_next_frame(pFlac)) { + break; // Couldn't read the next frame, so just break from the loop and return. + } + } else { + // Here is where we grab the samples and interleave them. + + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + drflac_uint64 totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + drflac_uint64 samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + + drflac_uint64 misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; + if (misalignedSampleCount > 0) { + drflac_uint64 misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); + samplesRead += misalignedSamplesRead; + samplesReadFromFrameSoFar += misalignedSamplesRead; + bufferOut += misalignedSamplesRead; + samplesToRead -= misalignedSamplesRead; + pFlac->currentSample += misalignedSamplesRead; + } + + + drflac_uint64 alignedSampleCountPerChannel = samplesToRead / channelCount; + if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) { + alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount; + } + + drflac_uint64 firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; + unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + int left = pDecodedSamples0[i]; + int side = pDecodedSamples1[i]; + int right = left - side; + + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples0[i]; + int right = pDecodedSamples1[i]; + int left = right + side; + + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples1[i]; + int mid = (((drflac_uint32)pDecodedSamples0[i]) << 1) | (side & 0x01); + + bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo + { + // Stereo optimized inner loop unroll. + const drflac_int32* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const drflac_int32* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } + else + { + // Generic interleaving. + for (drflac_uint64 i = 0; i < alignedSampleCountPerChannel; ++i) { + for (unsigned int j = 0; j < channelCount; ++j) { + bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample); + } + } + } + } break; + } + + drflac_uint64 alignedSamplesRead = alignedSampleCountPerChannel * channelCount; + samplesRead += alignedSamplesRead; + samplesReadFromFrameSoFar += alignedSamplesRead; + bufferOut += alignedSamplesRead; + samplesToRead -= alignedSamplesRead; + pFlac->currentSample += alignedSamplesRead; + pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; + + + // At this point we may still have some excess samples left to read. + if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) { + drflac_uint64 excessSamplesRead = 0; + if (samplesToRead < pFlac->currentFrame.samplesRemaining) { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut); + } else { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); + } + + samplesRead += excessSamplesRead; + samplesReadFromFrameSoFar += excessSamplesRead; + bufferOut += excessSamplesRead; + samplesToRead -= excessSamplesRead; + pFlac->currentSample += excessSamplesRead; + } + } + } + + return samplesRead; +} + +drflac_uint64 drflac_read_s16(drflac* pFlac, drflac_uint64 samplesToRead, drflac_int16* pBufferOut) +{ + // This reads samples in 2 passes and can probably be optimized. + drflac_uint64 totalSamplesRead = 0; + + while (samplesToRead > 0) { + drflac_int32 samples32[4096]; + drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); + if (samplesJustRead == 0) { + break; // Reached the end. + } + + // s32 -> s16 + for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { + pBufferOut[i] = (drflac_int16)(samples32[i] >> 16); + } + + totalSamplesRead += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; + } + + return totalSamplesRead; +} + +drflac_uint64 drflac_read_f32(drflac* pFlac, drflac_uint64 samplesToRead, float* pBufferOut) +{ + // This reads samples in 2 passes and can probably be optimized. + drflac_uint64 totalSamplesRead = 0; + + while (samplesToRead > 0) { + drflac_int32 samples32[4096]; + drflac_uint64 samplesJustRead = drflac_read_s32(pFlac, (samplesToRead > 4096) ? 4096 : samplesToRead, samples32); + if (samplesJustRead == 0) { + break; // Reached the end. + } + + // s32 -> f32 + for (drflac_uint64 i = 0; i < samplesJustRead; ++i) { + pBufferOut[i] = (float)(samples32[i] / 2147483648.0); + } + + totalSamplesRead += samplesJustRead; + samplesToRead -= samplesJustRead; + pBufferOut += samplesJustRead; + } + + return totalSamplesRead; +} + +drflac_bool32 drflac_seek_to_sample(drflac* pFlac, drflac_uint64 sampleIndex) +{ + if (pFlac == NULL) { + return DRFLAC_FALSE; + } + + // If we don't know where the first frame begins then we can't seek. This will happen when the STREAMINFO block was not present + // when the decoder was opened. + if (pFlac->firstFramePos == 0) { + return DRFLAC_FALSE; + } + + if (sampleIndex == 0) { + pFlac->currentSample = 0; + return drflac__seek_to_first_frame(pFlac); + } else { + drflac_bool32 wasSuccessful = DRFLAC_FALSE; + + // Clamp the sample to the end. + if (sampleIndex >= pFlac->totalSampleCount) { + sampleIndex = pFlac->totalSampleCount - 1; + } + + // If the target sample and the current sample are in the same frame we just move the position forward. + if (sampleIndex > pFlac->currentSample) { + // Forward. + drflac_uint32 offset = (drflac_uint32)(sampleIndex - pFlac->currentSample); + if (pFlac->currentFrame.samplesRemaining > offset) { + pFlac->currentFrame.samplesRemaining -= offset; + pFlac->currentSample = sampleIndex; + return DRFLAC_TRUE; + } + } else { + // Backward. + drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentSample - sampleIndex); + drflac_uint32 currentFrameSampleCount = pFlac->currentFrame.header.blockSize * drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + drflac_uint32 currentFrameSamplesConsumed = (drflac_uint32)(currentFrameSampleCount - pFlac->currentFrame.samplesRemaining); + if (currentFrameSamplesConsumed > offsetAbs) { + pFlac->currentFrame.samplesRemaining += offsetAbs; + pFlac->currentSample = sampleIndex; + return DRFLAC_TRUE; + } + } + + // Different techniques depending on encapsulation. Using the native FLAC seektable with Ogg encapsulation is a bit awkward so + // we'll instead use Ogg's natural seeking facility. + #ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + wasSuccessful = drflac_ogg__seek_to_sample(pFlac, sampleIndex); + } + else + #endif + { + // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. + wasSuccessful = drflac__seek_to_sample__seek_table(pFlac, sampleIndex); + if (!wasSuccessful) { + wasSuccessful = drflac__seek_to_sample__brute_force(pFlac, sampleIndex); + } + } + + pFlac->currentSample = sampleIndex; + return wasSuccessful; + } +} + + + +//// High Level APIs //// + +// I couldn't figure out where SIZE_MAX was defined for VC6. If anybody knows, let me know. +#if defined(_MSC_VER) && _MSC_VER <= 1200 +#ifdef DRFLAC_64BIT +#define SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) +#else +#define SIZE_MAX 0xFFFFFFFF +#endif +#endif + +// Using a macro as the definition of the drflac__full_decode_and_close_*() API family. Sue me. +#define DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(extension, type) \ +static type* drflac__full_decode_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalSampleCountOut)\ +{ \ + drflac_assert(pFlac != NULL); \ + \ + type* pSampleData = NULL; \ + drflac_uint64 totalSampleCount = pFlac->totalSampleCount; \ + \ + if (totalSampleCount == 0) { \ + type buffer[4096]; \ + \ + size_t sampleDataBufferSize = sizeof(buffer); \ + pSampleData = (type*)DRFLAC_MALLOC(sampleDataBufferSize); \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + drflac_uint64 samplesRead; \ + while ((samplesRead = (drflac_uint64)drflac_read_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) { \ + if (((totalSampleCount + samplesRead) * sizeof(type)) > sampleDataBufferSize) { \ + sampleDataBufferSize *= 2; \ + type* pNewSampleData = (type*)DRFLAC_REALLOC(pSampleData, sampleDataBufferSize); \ + if (pNewSampleData == NULL) { \ + DRFLAC_FREE(pSampleData); \ + goto on_error; \ + } \ + \ + pSampleData = pNewSampleData; \ + } \ + \ + drflac_copy_memory(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(type))); \ + totalSampleCount += samplesRead; \ + } \ + \ + /* At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to \ + protect those ears from random noise! */ \ + drflac_zero_memory(pSampleData + totalSampleCount, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(type))); \ + } else { \ + drflac_uint64 dataSize = totalSampleCount * sizeof(type); \ + if (dataSize > SIZE_MAX) { \ + goto on_error; /* The decoded data is too big. */ \ + } \ + \ + pSampleData = (type*)DRFLAC_MALLOC((size_t)dataSize); /* <-- Safe cast as per the check above. */ \ + if (pSampleData == NULL) { \ + goto on_error; \ + } \ + \ + totalSampleCount = drflac_read_##extension(pFlac, pFlac->totalSampleCount, pSampleData); \ + } \ + \ + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ + if (channelsOut) *channelsOut = pFlac->channels; \ + if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; \ + \ + drflac_close(pFlac); \ + return pSampleData; \ + \ +on_error: \ + drflac_close(pFlac); \ + return NULL; \ +} + +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s32, drflac_int32) +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(s16, drflac_int16) +DRFLAC_DEFINE_FULL_DECODE_AND_CLOSE(f32, float) + +drflac_int32* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + // Safety. + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +drflac_int16* drflac_open_and_decode_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + // Safety. + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); +} + +float* drflac_open_and_decode_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + // Safety. + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} + +#ifndef DR_FLAC_NO_STDIO +drflac_int32* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +drflac_int16* drflac_open_and_decode_file_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); +} + +float* drflac_open_and_decode_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} +#endif + +drflac_int32* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s32(pFlac, channels, sampleRate, totalSampleCount); +} + +drflac_int16* drflac_open_and_decode_memory_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_s16(pFlac, channels, sampleRate, totalSampleCount); +} + +float* drflac_open_and_decode_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close_f32(pFlac, channels, sampleRate, totalSampleCount); +} + +void drflac_free(void* pSampleDataReturnedByOpenAndDecode) +{ + DRFLAC_FREE(pSampleDataReturnedByOpenAndDecode); +} + + + + +void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const char* pComments) +{ + if (pIter == NULL) { + return; + } + + pIter->countRemaining = commentCount; + pIter->pRunningData = pComments; +} + +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) +{ + // Safety. + if (pCommentLengthOut) *pCommentLengthOut = 0; + + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return NULL; + } + + drflac_uint32 length = drflac__le2host_32(*(drflac_uint32*)pIter->pRunningData); + pIter->pRunningData += 4; + + const char* pComment = pIter->pRunningData; + pIter->pRunningData += length; + pIter->countRemaining -= 1; + + if (pCommentLengthOut) *pCommentLengthOut = length; + return pComment; +} +#endif //DR_FLAC_IMPLEMENTATION + + +// REVISION HISTORY +// +// v0.9.7 - 2018-07-05 +// - Fix a warning. +// +// v0.9.6 - 2018-06-29 +// - Fix some typos. +// +// v0.9.5 - 2018-06-23 +// - Fix some warnings. +// +// v0.9.4 - 2018-06-14 +// - Optimizations to seeking. +// - Clean up. +// +// v0.9.3 - 2018-05-22 +// - Bug fix. +// +// v0.9.2 - 2018-05-12 +// - Fix a compilation error due to a missing break statement. +// +// v0.9.1 - 2018-04-29 +// - Fix compilation error with Clang. +// +// v0.9 - 2018-04-24 +// - Fix Clang build. +// - Start using major.minor.revision versioning. +// +// v0.8g - 2018-04-19 +// - Fix build on non-x86/x64 architectures. +// +// v0.8f - 2018-02-02 +// - Stop pretending to support changing rate/channels mid stream. +// +// v0.8e - 2018-02-01 +// - Fix a crash when the block size of a frame is larger than the maximum block size defined by the FLAC stream. +// - Fix a crash the the Rice partition order is invalid. +// +// v0.8d - 2017-09-22 +// - Add support for decoding streams with ID3 tags. ID3 tags are just skipped. +// +// v0.8c - 2017-09-07 +// - Fix warning on non-x86/x64 architectures. +// +// v0.8b - 2017-08-19 +// - Fix build on non-x86/x64 architectures. +// +// v0.8a - 2017-08-13 +// - A small optimization for the Clang build. +// +// v0.8 - 2017-08-12 +// - API CHANGE: Rename dr_* types to drflac_*. +// - Optimizations. This brings dr_flac back to about the same class of efficiency as the reference implementation. +// - Add support for custom implementations of malloc(), realloc(), etc. +// - Add CRC checking to Ogg encapsulated streams. +// - Fix VC++ 6 build. This is only for the C++ compiler. The C compiler is not currently supported. +// - Bug fixes. +// +// v0.7 - 2017-07-23 +// - Add support for opening a stream without a header block. To do this, use drflac_open_relaxed() / drflac_open_with_metadata_relaxed(). +// +// v0.6 - 2017-07-22 +// - Add support for recovering from invalid frames. With this change, dr_flac will simply skip over invalid frames as if they +// never existed. Frames are checked against their sync code, the CRC-8 of the frame header and the CRC-16 of the whole frame. +// +// v0.5 - 2017-07-16 +// - Fix typos. +// - Change drflac_bool* types to unsigned. +// - Add CRC checking. This makes dr_flac slower, but can be disabled with #define DR_FLAC_NO_CRC. +// +// v0.4f - 2017-03-10 +// - Fix a couple of bugs with the bitstreaming code. +// +// v0.4e - 2017-02-17 +// - Fix some warnings. +// +// v0.4d - 2016-12-26 +// - Add support for 32-bit floating-point PCM decoding. +// - Use drflac_int*/drflac_uint* sized types to improve compiler support. +// - Minor improvements to documentation. +// +// v0.4c - 2016-12-26 +// - Add support for signed 16-bit integer PCM decoding. +// +// v0.4b - 2016-10-23 +// - A minor change to drflac_bool8 and drflac_bool32 types. +// +// v0.4a - 2016-10-11 +// - Rename drBool32 to drflac_bool32 for styling consistency. +// +// v0.4 - 2016-09-29 +// - API/ABI CHANGE: Use fixed size 32-bit booleans instead of the built-in bool type. +// - API CHANGE: Rename drflac_open_and_decode*() to drflac_open_and_decode*_s32(). +// - API CHANGE: Swap the order of "channels" and "sampleRate" parameters in drflac_open_and_decode*(). Rationale for this is to +// keep it consistent with drflac_audio. +// +// v0.3f - 2016-09-21 +// - Fix a warning with GCC. +// +// v0.3e - 2016-09-18 +// - Fixed a bug where GCC 4.3+ was not getting properly identified. +// - Fixed a few typos. +// - Changed date formats to ISO 8601 (YYYY-MM-DD). +// +// v0.3d - 2016-06-11 +// - Minor clean up. +// +// v0.3c - 2016-05-28 +// - Fixed compilation error. +// +// v0.3b - 2016-05-16 +// - Fixed Linux/GCC build. +// - Updated documentation. +// +// v0.3a - 2016-05-15 +// - Minor fixes to documentation. +// +// v0.3 - 2016-05-11 +// - Optimizations. Now at about parity with the reference implementation on 32-bit builds. +// - Lots of clean up. +// +// v0.2b - 2016-05-10 +// - Bug fixes. +// +// v0.2a - 2016-05-10 +// - Made drflac_open_and_decode() more robust. +// - Removed an unused debugging variable +// +// v0.2 - 2016-05-09 +// - Added support for Ogg encapsulation. +// - API CHANGE. Have the onSeek callback take a third argument which specifies whether or not the seek +// should be relative to the start or the current position. Also changes the seeking rules such that +// seeking offsets will never be negative. +// - Have drflac_open_and_decode() fail gracefully if the stream has an unknown total sample count. +// +// v0.1b - 2016-05-07 +// - Properly close the file handle in drflac_open_file() and family when the decoder fails to initialize. +// - Removed a stale comment. +// +// v0.1a - 2016-05-05 +// - Minor formatting changes. +// - Fixed a warning on the GCC build. +// +// v0.1 - 2016-05-03 +// - Initial versioned release. + + +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ diff --git a/SDL2_sound/dr_mp3.h b/SDL2_sound/dr_mp3.h new file mode 100644 index 00000000..74f328f4 --- /dev/null +++ b/SDL2_sound/dr_mp3.h @@ -0,0 +1,2832 @@ +// MP3 audio decoder. Public domain. See "unlicense" statement at the end of this file. +// dr_mp3 - v0.2.7 - 2018-07-13 +// +// David Reid - mackron@gmail.com +// +// Based off minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for +// differences between minimp3 and dr_mp3. + +// USAGE +// ===== +// dr_mp3 is a single-file library. To use it, do something like the following in one .c file. +// #define DR_MP3_IMPLEMENTATION +// #include "dr_mp3.h" +// +// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, +// do something like the following: +// +// drmp3 mp3; +// if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) { +// // Failed to open file +// } +// +// ... +// +// drmp3_uint64 framesRead = drmp3_read_f32(pMP3, framesToRead, pFrames); +// +// The drmp3 object is transparent so you can get access to the channel count and sample rate like so: +// +// drmp3_uint32 channels = mp3.channels; +// drmp3_uint32 sampleRate = mp3.sampleRate; +// +// The third parameter of drmp3_init_file() in the example above allows you to control the output channel count and sample rate. It +// is a pointer to a drmp3_config object. Setting any of the variables of this object to 0 will cause dr_mp3 to use defaults. +// +// The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek +// callbacks with drmp3_init_memory() and drmp3_init() respectively. +// +// You do need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request +// any number of PCM frames in each call to drmp3_read_f32() and it will return as many PCM frames as it can, up to the requested +// amount. +// +// You can also decode an entire file in one go with drmp3_open_and_decode_f32(), drmp3_open_and_decode_memory_f32() and +// drmp3_open_and_decode_file_f32(). +// +// +// OPTIONS +// ======= +// #define these options before including this file. +// +// #define DR_MP3_NO_STDIO +// Disable drmp3_init_file(), etc. +// +// #define DR_MP3_NO_SIMD +// Disable SIMD optimizations. +// +// +// LIMITATIONS +// =========== +// - Seeking is extremely inefficient. + +#ifndef dr_mp3_h +#define dr_mp3_h + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifdef __SDL_SOUND_INTERNAL__ +typedef Sint8 drmp3_int8; +typedef Uint8 drmp3_uint8; +typedef Sint16 drmp3_int16; +typedef Uint16 drmp3_uint16; +typedef Sint32 drmp3_int32; +typedef Uint32 drmp3_uint32; +typedef Sint64 drmp3_int64; +typedef Uint64 drmp3_uint64; +#elif defined(_MSC_VER) && _MSC_VER < 1600 +typedef signed char drmp3_int8; +typedef unsigned char drmp3_uint8; +typedef signed short drmp3_int16; +typedef unsigned short drmp3_uint16; +typedef signed int drmp3_int32; +typedef unsigned int drmp3_uint32; +typedef signed __int64 drmp3_int64; +typedef unsigned __int64 drmp3_uint64; +#else +#include +typedef int8_t drmp3_int8; +typedef uint8_t drmp3_uint8; +typedef int16_t drmp3_int16; +typedef uint16_t drmp3_uint16; +typedef int32_t drmp3_int32; +typedef uint32_t drmp3_uint32; +typedef int64_t drmp3_int64; +typedef uint64_t drmp3_uint64; +#endif +typedef drmp3_uint8 drmp3_bool8; +typedef drmp3_uint32 drmp3_bool32; +#define DRMP3_TRUE 1 +#define DRMP3_FALSE 0 + +#define DRMP3_MAX_SAMPLES_PER_FRAME (1152*2) + + +// Low Level Push API +// ================== +typedef struct +{ + int frame_bytes, channels, hz, layer, bitrate_kbps; +} drmp3dec_frame_info; + +typedef struct +{ + float mdct_overlap[2][9*32], qmf_state[15*2*32]; + int reserv, free_format_bytes; + unsigned char header[4], reserv_buf[511]; +} drmp3dec; + +// Initializes a low level decoder. +void drmp3dec_init(drmp3dec *dec); + +// Reads a frame from a low level decoder. +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info); + + +// Main API (Pull API) +// =================== + +typedef struct drmp3_src drmp3_src; +typedef drmp3_uint64 (* drmp3_src_read_proc)(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read. + +typedef enum +{ + drmp3_src_algorithm_none, + drmp3_src_algorithm_linear +} drmp3_src_algorithm; + +#define DRMP3_SRC_CACHE_SIZE_IN_FRAMES 512 +typedef struct +{ + drmp3_src* pSRC; + float pCachedFrames[2 * DRMP3_SRC_CACHE_SIZE_IN_FRAMES]; + drmp3_uint32 cachedFrameCount; + drmp3_uint32 iNextFrame; +} drmp3_src_cache; + +typedef struct +{ + drmp3_uint32 sampleRateIn; + drmp3_uint32 sampleRateOut; + drmp3_uint32 channels; + drmp3_src_algorithm algorithm; + drmp3_uint32 cacheSizeInFrames; // The number of frames to read from the client at a time. +} drmp3_src_config; + +struct drmp3_src +{ + drmp3_src_config config; + drmp3_src_read_proc onRead; + void* pUserData; + float bin[256]; + drmp3_src_cache cache; // <-- For simplifying and optimizing client -> memory reading. + union + { + struct + { + float alpha; + drmp3_bool32 isPrevFramesLoaded : 1; + drmp3_bool32 isNextFramesLoaded : 1; + } linear; + } algo; +}; + +typedef enum +{ + drmp3_seek_origin_start, + drmp3_seek_origin_current +} drmp3_seek_origin; + +// Callback for when data is read. Return value is the number of bytes actually read. +// +// pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. +// pBufferOut [out] The output buffer. +// bytesToRead [in] The number of bytes to read. +// +// Returns the number of bytes actually read. +// +// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until +// either the entire bytesToRead is filled or you have reached the end of the stream. +typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +// Callback for when data needs to be seeked. +// +// pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family. +// offset [in] The number of bytes to move, relative to the origin. Will never be negative. +// origin [in] The origin of the seek - the current position or the start of the stream. +// +// Returns whether or not the seek was successful. +// +// Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which +// will be either drmp3_seek_origin_start or drmp3_seek_origin_current. +typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); + +typedef struct +{ + drmp3_uint32 outputChannels; + drmp3_uint32 outputSampleRate; +} drmp3_config; + +typedef struct +{ + drmp3dec decoder; + drmp3dec_frame_info frameInfo; + drmp3_uint32 channels; + drmp3_uint32 sampleRate; + drmp3_read_proc onRead; + drmp3_seek_proc onSeek; + void* pUserData; + drmp3_uint32 frameChannels; // The number of channels in the currently loaded MP3 frame. Internal use only. + drmp3_uint32 frameSampleRate; // The sample rate of the currently loaded MP3 frame. Internal use only. + drmp3_uint32 framesConsumed; + drmp3_uint32 framesRemaining; + drmp3_int16 frames[DRMP3_MAX_SAMPLES_PER_FRAME]; + drmp3_src src; + size_t dataSize; + size_t dataCapacity; + drmp3_uint8* pData; + drmp3_bool32 atEnd : 1; + struct + { + const drmp3_uint8* pData; + size_t dataSize; + size_t currentReadPos; + } memory; // Only used for decoders that were opened against a block of memory. +} drmp3; + +// Initializes an MP3 decoder. +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +// +// Returns true if successful; false otherwise. +// +// Close the loader with drmp3_uninit(). +// +// See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit() +drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig); + +// Initializes an MP3 decoder from a block of memory. +// +// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +// the lifetime of the drmp3 object. +// +// The buffer should contain the contents of the entire MP3 file. +drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig); + +#ifndef DR_MP3_NO_STDIO +// Initializes an MP3 decoder from a file. +// +// This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3 +// objects because the operating system may restrict the number of file handles an application can have open at +// any given time. +drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig); +#endif + +// Uninitializes an MP3 decoder. +void drmp3_uninit(drmp3* pMP3); + +// Reads PCM frames as interleaved 32-bit IEEE floating point PCM. +// +// Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames. +drmp3_uint64 drmp3_read_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); + +// Seeks to a specific frame. +// +// Note that this is _not_ an MP3 frame, but rather a PCM frame. +drmp3_bool32 drmp3_seek_to_frame(drmp3* pMP3, drmp3_uint64 frameIndex); + + +// Opens an decodes an entire MP3 stream as a single operation. +// +// pConfig is both an input and output. On input it contains what you want. On output it contains what you got. +// +// Free the returned pointer with drmp3_free(). +float* drmp3_open_and_decode_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount); +float* drmp3_open_and_decode_memory_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount); +#ifndef DR_MP3_NO_STDIO +float* drmp3_open_and_decode_file_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount); +#endif + +// Frees any memory that was allocated by a public drmp3 API. +void drmp3_free(void* p); + +#ifdef __cplusplus +} +#endif +#endif // dr_mp3_h + + +///////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +///////////////////////////////////////////////////// +#ifdef DR_MP3_IMPLEMENTATION +#include +#include +#include +#include // For INT_MAX + +// Disable SIMD when compiling with TCC for now. +#if defined(__TINYC__) +#define DR_MP3_NO_SIMD +#endif + +#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */ +#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 + +#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE /* MUST be >= 320000/8/32000*1152 = 1440 */ + +#define DRMP3_MAX_BITRESERVOIR_BYTES 511 +#define DRMP3_SHORT_BLOCK_TYPE 2 +#define DRMP3_STOP_BLOCK_TYPE 3 +#define DRMP3_MODE_MONO 3 +#define DRMP3_MODE_JOINT_STEREO 1 +#define DRMP3_HDR_SIZE 4 +#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) + +#define DRMP3_BITS_DEQUANTIZER_OUT -1 +#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) + +#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) + +#if !defined(DR_MP3_NO_SIMD) + +#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(_M_ARM64) || defined(__x86_64__) || defined(__aarch64__)) +/* x64 always have SSE2, arm64 always have neon, no need for generic code */ +#define DR_MP3_ONLY_SIMD +#endif + +#if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__)) +#if defined(_MSC_VER) +#include +#endif +#include +#define DRMP3_HAVE_SSE 1 +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE _mm_storeu_ps +#define DRMP3_VLD _mm_loadu_ps +#define DRMP3_VSET _mm_set1_ps +#define DRMP3_VADD _mm_add_ps +#define DRMP3_VSUB _mm_sub_ps +#define DRMP3_VMUL _mm_mul_ps +#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 drmp3_f4; +#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) +#define drmp3_cpuid __cpuid +#else +static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +{ +#if defined(__PIC__) + __asm__ __volatile__( +#if defined(__x86_64__) + "push %%rbx\n" + "cpuid\n" + "xchgl %%ebx, %1\n" + "pop %%rbx\n" +#else + "xchgl %%ebx, %1\n" + "cpuid\n" + "xchgl %%ebx, %1\n" +#endif + : "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#else + __asm__ __volatile__( + "cpuid" + : "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3]) + : "a" (InfoType)); +#endif +} +#endif +static int drmp3_have_simd() +{ +#ifdef DR_MP3_ONLY_SIMD + return 1; +#else + static int g_have_simd; + int CPUInfo[4]; +#ifdef MINIMP3_TEST + static int g_counter; + if (g_counter++ > 100) + return 0; +#endif + if (g_have_simd) + goto end; + drmp3_cpuid(CPUInfo, 0); + if (CPUInfo[0] > 0) + { + drmp3_cpuid(CPUInfo, 1); + g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; /* SSE2 */ + return g_have_simd - 1; + } + +end: + return g_have_simd - 1; +#endif +} +#elif defined(__ARM_NEON) || defined(__aarch64__) +#include +#define DRMP3_HAVE_SIMD 1 +#define DRMP3_VSTORE vst1q_f32 +#define DRMP3_VLD vld1q_f32 +#define DRMP3_VSET vmovq_n_f32 +#define DRMP3_VADD vaddq_f32 +#define DRMP3_VSUB vsubq_f32 +#define DRMP3_VMUL vmulq_f32 +#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t drmp3_f4; +static int drmp3_have_simd() +{ /* TODO: detect neon for !DR_MP3_ONLY_SIMD */ + return 1; +} +#else +#define DRMP3_HAVE_SIMD 0 +#ifdef DR_MP3_ONLY_SIMD +#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#endif +#endif + +#else + +#define DRMP3_HAVE_SIMD 0 + +#endif + +typedef struct +{ + const drmp3_uint8 *buf; + int pos, limit; +} drmp3_bs; + +typedef struct +{ + float scf[3*64]; + drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} drmp3_L12_scale_info; + +typedef struct +{ + drmp3_uint8 tab_offset, code_tab_width, band_count; +} drmp3_L12_subband_alloc; + +typedef struct +{ + const drmp3_uint8 *sfbtab; + drmp3_uint16 part_23_length, big_values, scalefac_compress; + drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; + drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; +} drmp3_L3_gr_info; + +typedef struct +{ + drmp3_bs bs; + drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + drmp3_L3_gr_info gr_info[4]; + float grbuf[2][576], scf[40], syn[18 + 15][2*32]; + drmp3_uint8 ist_pos[2][39]; +} drmp3dec_scratch; + +static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) +{ + bs->buf = data; + bs->pos = 0; + bs->limit = bytes*8; +} + +static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) +{ + drmp3_uint32 next, cache = 0, s = bs->pos & 7; + int shl = n + s; + const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); + if ((bs->pos += n) > bs->limit) + return 0; + next = *p++ & (255 >> s); + while ((shl -= 8) > 0) + { + cache |= next << shl; + next = *p++; + } + return cache | (next >> -shl); +} + +static int drmp3_hdr_valid(const drmp3_uint8 *h) +{ + return h[0] == 0xff && + ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && + (DRMP3_HDR_GET_LAYER(h) != 0) && + (DRMP3_HDR_GET_BITRATE(h) != 15) && + (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); +} + +static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) +{ + return drmp3_hdr_valid(h2) && + ((h1[1] ^ h2[1]) & 0xFE) == 0 && + ((h1[2] ^ h2[2]) & 0x0C) == 0 && + !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); +} + +static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) +{ + static const drmp3_uint8 halfrate[2][3][15] = { + { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, + { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, + }; + return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; +} + +static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) +{ + static const unsigned g_hz[3] = { 44100, 48000, 32000 }; + return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); +} + +static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) +{ + return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); +} + +static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) +{ + int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); + if (DRMP3_HDR_IS_LAYER_1(h)) + { + frame_bytes &= ~3; /* slot align */ + } + return frame_bytes ? frame_bytes : free_format_size; +} + +static int drmp3_hdr_padding(const drmp3_uint8 *h) +{ + return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; +} + +#ifndef DR_MP3_ONLY_MP3 +static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) +{ + const drmp3_L12_subband_alloc *alloc; + int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + + if (DRMP3_HDR_IS_LAYER_1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + alloc = g_alloc_L1; + nbands = 32; + } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + alloc = g_alloc_L2M2; + nbands = 30; + } else + { + static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); + if (!kbps) /* free-format */ + { + kbps = 192; + } + + alloc = g_alloc_L2M1; + nbands = 27; + if (kbps < 56) + { + static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + alloc = g_alloc_L2M1_lowrate; + nbands = sample_rate_idx == 2 ? 12 : 8; + } else if (kbps >= 96 && sample_rate_idx != 1) + { + nbands = 30; + } + } + + sci->total_bands = (drmp3_uint8)nbands; + sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); + + return alloc; +} + +static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) +{ + static const float g_deq_L12[18*3] = { +#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) + }; + int i, m; + for (i = 0; i < bands; i++) + { + float s = 0; + int ba = *pba++; + int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0; + for (m = 4; m; m >>= 1) + { + if (mask & m) + { + int b = drmp3_bs_get_bits(bs, 6); + s = g_deq_L12[ba*3 - 6 + b % 3]*(1 << 21 >> b/3); + } + *scf++ = s; + } + } +} + +static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) +{ + static const drmp3_uint8 g_bitalloc_code_tab[] = { + 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, + 0,17,18, 3,19,4,5,16, + 0,17,18,16, + 0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15, + 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, + 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 + }; + const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); + + int i, k = 0, ba_bits = 0; + const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; + + for (i = 0; i < sci->total_bands; i++) + { + drmp3_uint8 ba; + if (i == k) + { + k += subband_alloc->band_count; + ba_bits = subband_alloc->code_tab_width; + ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; + subband_alloc++; + } + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + sci->bitalloc[2*i] = ba; + if (i < sci->stereo_bands) + { + ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + } + sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; + } + + for (i = 0; i < 2*sci->total_bands; i++) + { + sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); + } + + drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + + for (i = sci->stereo_bands; i < sci->total_bands; i++) + { + sci->bitalloc[2*i + 1] = 0; + } +} + +static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) +{ + int i, j, k, choff = 576; + for (j = 0; j < 4; j++) + { + float *dst = grbuf + group_size*j; + for (i = 0; i < 2*sci->total_bands; i++) + { + int ba = sci->bitalloc[i]; + if (ba != 0) + { + if (ba < 17) + { + int half = (1 << (ba - 1)) - 1; + for (k = 0; k < group_size; k++) + { + dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); + } + } else + { + unsigned mod = (2 << (ba - 17)) + 1; /* 3, 5, 9 */ + unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); /* 5, 7, 10 */ + for (k = 0; k < group_size; k++, code /= mod) + { + dst[k] = (float)((int)(code % mod - mod/2)); + } + } + } + dst += choff; + choff = 18 - choff; + } + } + return group_size*4; +} + +static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) +{ + int i, k; + memcpy(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) + { + for (k = 0; k < 12; k++) + { + dst[k + 0] *= scf[0]; + dst[k + 576] *= scf[3]; + } + } +} +#endif + +static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + static const drmp3_uint8 g_scf_long[8][23] = { + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 }, + { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, + { 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 }, + { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, + { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } + }; + static const drmp3_uint8 g_scf_short[8][40] = { + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + static const drmp3_uint8 g_scf_mixed[8][40] = { + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, + { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 }, + { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, + { 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } + }; + + unsigned tables, scfsi = 0; + int main_data_begin, part_23_sum = 0; + int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + gr_count *= 2; + main_data_begin = drmp3_bs_get_bits(bs, 9); + scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); + } else + { + main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + } + + do + { + if (DRMP3_HDR_IS_MONO(hdr)) + { + scfsi <<= 4; + } + gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); + part_23_sum += gr->part_23_length; + gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); + if (gr->big_values > 288) + { + return -1; + } + gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->sfbtab = g_scf_long[sr_idx]; + gr->n_long_sfb = 22; + gr->n_short_sfb = 0; + if (drmp3_bs_get_bits(bs, 1)) + { + gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); + if (!gr->block_type) + { + return -1; + } + gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->region_count[0] = 7; + gr->region_count[1] = 255; + if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) + { + scfsi &= 0x0F0F; + if (!gr->mixed_block_flag) + { + gr->region_count[0] = 8; + gr->sfbtab = g_scf_short[sr_idx]; + gr->n_long_sfb = 0; + gr->n_short_sfb = 39; + } else + { + gr->sfbtab = g_scf_mixed[sr_idx]; + gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_short_sfb = 30; + } + } + tables = drmp3_bs_get_bits(bs, 10); + tables <<= 5; + gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + } else + { + gr->block_type = 0; + gr->mixed_block_flag = 0; + tables = drmp3_bs_get_bits(bs, 15); + gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); + gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->region_count[2] = 255; + } + gr->table_select[0] = (drmp3_uint8)(tables >> 10); + gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); + gr->table_select[2] = (drmp3_uint8)((tables) & 31); + gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); + scfsi <<= 4; + gr++; + } while(--gr_count); + + if (part_23_sum + bs->pos > bs->limit + main_data_begin*8) + { + return -1; + } + + return main_data_begin; +} + +static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) +{ + int i, k; + for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) + { + int cnt = scf_count[i]; + if (scfsi & 8) + { + memcpy(scf, ist_pos, cnt); + } else + { + int bits = scf_size[i]; + if (!bits) + { + memset(scf, 0, cnt); + memset(ist_pos, 0, cnt); + } else + { + int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; + for (k = 0; k < cnt; k++) + { + int s = drmp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); + scf[k] = (drmp3_uint8)s; + } + } + } + ist_pos += cnt; + scf += cnt; + } + scf[0] = scf[1] = scf[2] = 0; +} + +static float drmp3_L3_ldexp_q2(float y, int exp_q2) +{ + static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; + int e; + do + { + e = DRMP3_MIN(30*4, exp_q2); + y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); + } while ((exp_q2 -= e) > 0); + return y; +} + +static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) +{ + static const drmp3_uint8 g_scf_partitions[3][28] = { + { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, + { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, + { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } + }; + const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + drmp3_uint8 scf_size[4], iscf[40]; + int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; + float gain; + + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + int part = g_scfc_decode[gr->scalefac_compress]; + scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); + } else + { + static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; + sfc = gr->scalefac_compress >> ist; + for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) + { + for (modprod = 1, i = 3; i >= 0; i--) + { + scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); + modprod *= g_mod[k + i]; + } + } + scf_partition += k; + scfsi = -16; + } + drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + + if (gr->n_short_sfb) + { + int sh = 3 - scf_shift; + for (i = 0; i < gr->n_short_sfb; i += 3) + { + iscf[gr->n_long_sfb + i + 0] += gr->subblock_gain[0] << sh; + iscf[gr->n_long_sfb + i + 1] += gr->subblock_gain[1] << sh; + iscf[gr->n_long_sfb + i + 2] += gr->subblock_gain[2] << sh; + } + } else if (gr->preflag) + { + static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + for (i = 0; i < 10; i++) + { + iscf[11 + i] += g_preamp[i]; + } + } + + gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); + for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) + { + scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + } +} + +static const float g_drmp3_pow43[129 + 16] = { + 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, + 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f +}; + +static float drmp3_L3_pow_43(int x) +{ + float frac; + int sign, mult = 256; + + if (x < 129) + { + return g_drmp3_pow43[16 + x]; + } + + if (x < 1024) + { + mult = 16; + x <<= 3; + } + + sign = 2*x & 64; + frac = (float)((x & 63) - sign) / ((x & ~63) + sign); + return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; +} + +static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +{ + static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, + -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, + -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, + -253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258, + -254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259, + -252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258, + -252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258, + -253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259, + -251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258, + -251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290, + -252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259, + -250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258, + -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, + -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, + -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; + static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; + +#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - n)) +#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + + float one = 0.0f; + int ireg = 0, big_val_cnt = gr_info->big_values; + const drmp3_uint8 *sfb = gr_info->sfbtab; + const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; + bs_next_ptr += 4; + + while (big_val_cnt > 0) + { + int tab_num = gr_info->table_select[ireg]; + int sfb_cnt = gr_info->region_count[ireg++]; + const short *codebook = tabs + tabindex[tab_num]; + int linbits = g_linbits[tab_num]; + do + { + np = *sfb++ / 2; + pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + one = *scf++; + do + { + int j, w = 5; + int leaf = codebook[DRMP3_PEEK_BITS(w)]; + while (leaf < 0) + { + DRMP3_FLUSH_BITS(w); + w = leaf & 7; + leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + } + DRMP3_FLUSH_BITS(leaf >> 8); + + for (j = 0; j < 2; j++, dst++, leaf >>= 4) + { + int lsb = leaf & 0x0F; + if (lsb == 15 && linbits) + { + lsb += DRMP3_PEEK_BITS(linbits); + DRMP3_FLUSH_BITS(linbits); + DRMP3_CHECK_BITS; + *dst = one*drmp3_L3_pow_43(lsb)*((int32_t)bs_cache < 0 ? -1: 1); + } else + { + *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + } + DRMP3_FLUSH_BITS(lsb ? 1 : 0); + } + DRMP3_CHECK_BITS; + } while (--pairs_to_decode); + } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); + } + + for (np = 1 - big_val_cnt;; dst += 4) + { + const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; + if (!(leaf & 8)) + { + leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; + } + DRMP3_FLUSH_BITS(leaf & 7); + if (DRMP3_BSPOS > layer3gr_limit) + { + break; + } +#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(0); + DRMP3_DEQ_COUNT1(1); + DRMP3_RELOAD_SCALEFACTOR; + DRMP3_DEQ_COUNT1(2); + DRMP3_DEQ_COUNT1(3); + DRMP3_CHECK_BITS; + } + + bs->pos = layer3gr_limit; +} + +static void drmp3_L3_midside_stereo(float *left, int n) +{ + int i = 0; + float *right = left + 576; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < n - 3; i += 4) + { + drmp3_f4 vl = DRMP3_VLD(left + i); + drmp3_f4 vr = DRMP3_VLD(right + i); + DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); + DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); + } +#endif + for (; i < n; i++) + { + float a = left[i]; + float b = right[i]; + left[i] = a + b; + right[i] = a - b; + } +} + +static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +{ + int i; + for (i = 0; i < n; i++) + { + left[i + 576] = left[i]*kr; + left[i] = left[i]*kl; + } +} + +static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) +{ + int i, k; + + max_band[0] = max_band[1] = max_band[2] = -1; + + for (i = 0; i < nbands; i++) + { + for (k = 0; k < sfb[i]; k += 2) + { + if (right[k] != 0 || right[k + 1] != 0) + { + max_band[i % 3] = i; + break; + } + } + right += sfb[i]; + } +} + +static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) +{ + static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; + unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + + for (i = 0; sfb[i]; i++) + { + unsigned ipos = ist_pos[i]; + if ((int)i > max_band[i % 3] && ipos < max_pos) + { + float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (DRMP3_HDR_TEST_MPEG1(hdr)) + { + kl = g_pan[2*ipos]; + kr = g_pan[2*ipos + 1]; + } else + { + kl = 1; + kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + if (ipos & 1) + { + kl = kr; + kr = 1; + } + } + drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) + { + drmp3_L3_midside_stereo(left, sfb[i]); + } + left += sfb[i]; + } +} + +static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +{ + int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; + int i, max_blocks = gr->n_short_sfb ? 3 : 1; + + drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + if (gr->n_long_sfb) + { + max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); + } + for (i = 0; i < max_blocks; i++) + { + int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int itop = n_sfb - max_blocks + i; + int prev = itop - max_blocks; + ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + } + drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); +} + +static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) +{ + int i, len; + float *src = grbuf, *dst = scratch; + + for (;0 != (len = *sfb); sfb += 3, src += 2*len) + { + for (i = 0; i < len; i++, src++) + { + *dst++ = src[0*len]; + *dst++ = src[1*len]; + *dst++ = src[2*len]; + } + } + memcpy(grbuf, scratch, (dst - scratch)*sizeof(float)); +} + +static void drmp3_L3_antialias(float *grbuf, int nbands) +{ + static const float g_aa[2][8] = { + {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, + {0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f} + }; + + for (; nbands > 0; nbands--, grbuf += 18) + { + int i = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); + drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); + drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); + drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); + vd = DRMP3_VREV(vd); + DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); + vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); + } +#endif +#ifndef DR_MP3_ONLY_SIMD + for(; i < 8; i++) + { + float u = grbuf[18 + i]; + float d = grbuf[17 - i]; + grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i]; + grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i]; + } +#endif + } +} + +static void drmp3_L3_dct3_9(float *y) +{ + float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; + + s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; + t0 = s0 + s6*0.5f; + s0 -= s6; + t4 = (s4 + s2)*0.93969262f; + t2 = (s8 + s2)*0.76604444f; + s6 = (s4 - s8)*0.17364818f; + s4 += s8 - s2; + + s2 = s0 - s4*0.5f; + y[4] = s4 + s0; + s8 = t0 - t2 + s6; + s0 = t0 - t4 + t2; + s4 = t0 + t4 - s6; + + s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7]; + + s3 *= 0.86602540f; + t0 = (s5 + s1)*0.98480775f; + t4 = (s5 - s7)*0.34202014f; + t2 = (s1 + s7)*0.64278761f; + s1 = (s1 - s5 - s7)*0.86602540f; + + s5 = t0 - s3 - t2; + s7 = t4 - s3 - t0; + s3 = t4 + s3 - t2; + + y[0] = s4 - s7; + y[1] = s2 + s1; + y[2] = s0 - s3; + y[3] = s8 + s5; + y[5] = s8 - s5; + y[6] = s0 + s3; + y[7] = s2 - s1; + y[8] = s4 + s7; +} + +static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +{ + int i, j; + static const float g_twid9[18] = { + 0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f + }; + + for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9) + { + float co[9], si[9]; + co[0] = -grbuf[0]; + si[0] = grbuf[17]; + for (i = 0; i < 4; i++) + { + si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2]; + co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2]; + si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; + co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); + } + drmp3_L3_dct3_9(co); + drmp3_L3_dct3_9(si); + + si[1] = -si[1]; + si[3] = -si[3]; + si[5] = -si[5]; + si[7] = -si[7]; + + i = 0; + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; i < 8; i += 4) + { + drmp3_f4 vovl = DRMP3_VLD(overlap + i); + drmp3_f4 vc = DRMP3_VLD(co + i); + drmp3_f4 vs = DRMP3_VLD(si + i); + drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); + drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); + drmp3_f4 vw0 = DRMP3_VLD(window + i); + drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); + drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); + DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); + DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); + vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); + DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); + } +#endif + for (; i < 9; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i]; + overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i]; + grbuf[i] = ovl*window[0 + i] - sum*window[9 + i]; + grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i]; + } + } +} + +static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) +{ + float m1 = x1*0.86602540f; + float a1 = x0 - x2*0.5f; + dst[1] = x0 + x2; + dst[0] = a1 + m1; + dst[2] = a1 - m1; +} + +static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) +{ + static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; + float co[3], si[3]; + int i; + + drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + si[1] = -si[1]; + + for (i = 0; i < 3; i++) + { + float ovl = overlap[i]; + float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i]; + overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i]; + dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i]; + dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; + } +} + +static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +{ + for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) + { + float tmp[18]; + memcpy(tmp, grbuf, sizeof(tmp)); + memcpy(grbuf, overlap, 6*sizeof(float)); + drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + } +} + +static void drmp3_L3_change_sign(float *grbuf) +{ + int b, i; + for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) + for (i = 1; i < 18; i += 2) + grbuf[i] = -grbuf[i]; +} + +static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +{ + static const float g_mdct_window[2][18] = { + { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, + { 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f } + }; + if (n_long_bands) + { + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + grbuf += 18*n_long_bands; + overlap += 9*n_long_bands; + } + if (block_type == DRMP3_SHORT_BLOCK_TYPE) + drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + else + drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); +} + +static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) +{ + int pos = (s->bs.pos + 7)/8u; + int remains = s->bs.limit/8u - pos; + if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) + { + pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; + remains = DRMP3_MAX_BITRESERVOIR_BYTES; + } + if (remains > 0) + { + memmove(h->reserv_buf, s->maindata + pos, remains); + } + h->reserv = remains; +} + +static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) +{ + int frame_bytes = (bs->limit - bs->pos)/8; + int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); + memcpy(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); + memcpy(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + return h->reserv >= main_data_begin; +} + +static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) +{ + int ch; + + for (ch = 0; ch < nch; ch++) + { + int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; + drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + } + + if (DRMP3_HDR_TEST_I_STEREO(h->header)) + { + drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) + { + drmp3_L3_midside_stereo(s->grbuf[0], 576); + } + + for (ch = 0; ch < nch; ch++, gr_info++) + { + int aa_bands = 31; + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + + if (gr_info->n_short_sfb) + { + aa_bands = n_long_bands - 1; + drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + } + + drmp3_L3_antialias(s->grbuf[ch], aa_bands); + drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + drmp3_L3_change_sign(s->grbuf[ch]); + } +} + +static void drmp3d_DCT_II(float *grbuf, int n) +{ + static const float g_sec[24] = { + 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f + }; + int i, k = 0; +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (; k < n; k += 4) + { + drmp3_f4 t[4][8], *x; + float *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); + drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); + drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); + drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); + drmp3_f4 t0 = DRMP3_VADD(x0, x3); + drmp3_f4 t1 = DRMP3_VADD(x1, x2); + drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); + drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = DRMP3_VADD(t0, t1); + x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = DRMP3_VADD(t3, t2); + x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); + x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); + x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); + x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); + x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); + x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); + x[0] = DRMP3_VADD(x0, x1); + x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); + x5 = DRMP3_VADD(x5, x6); + x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); + x7 = DRMP3_VADD(x7, xt); + x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); /* rotate by PI/8 */ + x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); + x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); + x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); + x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); + x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); + x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); + x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); + x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); + x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); + } + + if (k > n - 3) + { +#if DRMP3_HAVE_SSE +#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#else +#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[i*18], vget_low_f32(v)) +#endif + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE2(0, t[0][i]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE2(0, t[0][7]); + DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE2(2, t[1][7]); + DRMP3_VSAVE2(3, t[3][7]); + } else + { +#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[i*18], v) + for (i = 0; i < 7; i++, y += 4*18) + { + drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); + DRMP3_VSAVE4(0, t[0][i]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); + DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); + DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); + } + DRMP3_VSAVE4(0, t[0][7]); + DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); + DRMP3_VSAVE4(2, t[1][7]); + DRMP3_VSAVE4(3, t[3][7]); + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} +#else + for (; k < n; k++) + { + float t[4][8], *x, *y = grbuf + k; + + for (x = t[0], i = 0; i < 8; i++, x++) + { + float x0 = y[i*18]; + float x1 = y[(15 - i)*18]; + float x2 = y[(16 + i)*18]; + float x3 = y[(31 - i)*18]; + float t0 = x0 + x3; + float t1 = x1 + x2; + float t2 = (x1 - x2)*g_sec[3*i + 0]; + float t3 = (x0 - x3)*g_sec[3*i + 1]; + x[0] = t0 + t1; + x[8] = (t0 - t1)*g_sec[3*i + 2]; + x[16] = t3 + t2; + x[24] = (t3 - t2)*g_sec[3*i + 2]; + } + for (x = t[0], i = 0; i < 4; i++, x += 8) + { + float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = x0 - x7; x0 += x7; + x7 = x1 - x6; x1 += x6; + x6 = x2 - x5; x2 += x5; + x5 = x3 - x4; x3 += x4; + x4 = x0 - x3; x0 += x3; + x3 = x1 - x2; x1 += x2; + x[0] = x0 + x1; + x[4] = (x0 - x1)*0.70710677f; + x5 = x5 + x6; + x6 = (x6 + x7)*0.70710677f; + x7 = x7 + xt; + x3 = (x3 + x4)*0.70710677f; + x5 -= x7*0.198912367f; /* rotate by PI/8 */ + x7 += x5*0.382683432f; + x5 -= x7*0.198912367f; + x0 = xt - x6; xt += x6; + x[1] = (xt + x7)*0.50979561f; + x[2] = (x4 + x3)*0.54119611f; + x[3] = (x0 - x5)*0.60134488f; + x[5] = (x0 + x5)*0.89997619f; + x[6] = (x4 - x3)*1.30656302f; + x[7] = (xt - x7)*2.56291556f; + + } + for (i = 0; i < 7; i++, y += 4*18) + { + y[0*18] = t[0][i]; + y[1*18] = t[2][i] + t[3][i] + t[3][i + 1]; + y[2*18] = t[1][i] + t[1][i + 1]; + y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1]; + } + y[0*18] = t[0][7]; + y[1*18] = t[2][7] + t[3][7]; + y[2*18] = t[1][7]; + y[3*18] = t[3][7]; + } +#endif +} + +static short drmp3d_scale_pcm(float sample) +{ + if (sample > 32767.0) return (short) 32767; + if (sample < -32768.0) return (short)-32768; + int s = (int)(sample + .5f); + s -= (s < 0); /* away from zero, to be compliant */ + if (s > 32767) return (short) 32767; + if (s < -32768) return (short)-32768; + return (short)s; +} + +static void drmp3d_synth_pair(short *pcm, int nch, const float *z) +{ + float a; + a = (z[14*64] - z[ 0]) * 29; + a += (z[ 1*64] + z[13*64]) * 213; + a += (z[12*64] - z[ 2*64]) * 459; + a += (z[ 3*64] + z[11*64]) * 2037; + a += (z[10*64] - z[ 4*64]) * 5153; + a += (z[ 5*64] + z[ 9*64]) * 6574; + a += (z[ 8*64] - z[ 6*64]) * 37489; + a += z[ 7*64] * 75038; + pcm[0] = drmp3d_scale_pcm(a); + + z += 2; + a = z[14*64] * 104; + a += z[12*64] * 1567; + a += z[10*64] * 9727; + a += z[ 8*64] * 64019; + a += z[ 6*64] * -9975; + a += z[ 4*64] * -45; + a += z[ 2*64] * 146; + a += z[ 0*64] * -5; + pcm[16*nch] = drmp3d_scale_pcm(a); +} + +static void drmp3d_synth(float *xl, short *dstl, int nch, float *lins) +{ + int i; + float *xr = xl + 576*(nch - 1); + short *dstr = dstl + (nch - 1); + + static const float g_win[] = { + -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, + -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, + -1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630, + -1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313, + -1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908, + -1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415, + -2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835, + -2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169, + -2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420, + -2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590, + -3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679, + -3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692, + -4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629, + -4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494, + -5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290 + }; + float *zlin = lins + 15*64; + const float *w = g_win; + + zlin[4*15] = xl[18*16]; + zlin[4*15 + 1] = xr[18*16]; + zlin[4*15 + 2] = xl[0]; + zlin[4*15 + 3] = xr[0]; + + zlin[4*31] = xl[1 + 18*16]; + zlin[4*31 + 1] = xr[1 + 18*16]; + zlin[4*31 + 2] = xl[1]; + zlin[4*31 + 3] = xr[1]; + + drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + drmp3d_synth_pair(dstl, nch, lins + 4*15); + drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); + +#if DRMP3_HAVE_SIMD + if (drmp3_have_simd()) for (i = 14; i >= 0; i--) + { +#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } +#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } +#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } + drmp3_f4 a, b; + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*i + 64] = xl[1 + 18*(1 + i)]; + zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; + zlin[4*i - 64 + 2] = xl[18*(1 + i)]; + zlin[4*i - 64 + 3] = xr[18*(1 + i)]; + + DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) + + { +#if DRMP3_HAVE_SSE + static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; + __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), + _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); + dstr[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (short)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (short)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (short)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (short)_mm_extract_epi16(pcm8, 6); +#else + int16x4_t pcma, pcmb; + a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); + b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); + vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); + vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); + vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0); + vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3); + vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3); + vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2); + vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); +#endif + } + } else +#endif +#ifdef DR_MP3_ONLY_SIMD + {} +#else + for (i = 14; i >= 0; i--) + { +#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } + float a[4], b[4]; + + zlin[4*i] = xl[18*(31 - i)]; + zlin[4*i + 1] = xr[18*(31 - i)]; + zlin[4*i + 2] = xl[1 + 18*(31 - i)]; + zlin[4*i + 3] = xr[1 + 18*(31 - i)]; + zlin[4*(i + 16)] = xl[1 + 18*(1 + i)]; + zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; + zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; + zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; + + DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) + + dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); + } +#endif +} + +static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, short *pcm, float *lins) +{ + int i; + for (i = 0; i < nch; i++) + { + drmp3d_DCT_II(grbuf + 576*i, nbands); + } + + memcpy(lins, qmf_state, sizeof(float)*15*64); + + for (i = 0; i < nbands; i += 2) + { + drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + } +#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL + if (nch == 1) + { + for (i = 0; i < 15*64; i += 2) + { + qmf_state[i] = lins[nbands*64 + i]; + } + } else +#endif + { + memcpy(qmf_state, lins + nbands*64, sizeof(float)*15*64); + } +} + +static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) +{ + int i, nmatch; + for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + { + i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); + if (i + DRMP3_HDR_SIZE > mp3_bytes) + return nmatch > 0; + if (!drmp3_hdr_compare(hdr, hdr + i)) + return 0; + } + return 1; +} + +static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +{ + int i, k; + for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) + { + if (drmp3_hdr_valid(mp3)) + { + int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); + + for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) + { + if (drmp3_hdr_compare(mp3, mp3 + k)) + { + int fb = k - drmp3_hdr_padding(mp3); + int nextfb = fb + drmp3_hdr_padding(mp3 + k); + if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) + continue; + frame_and_padding = k; + frame_bytes = fb; + *free_format_bytes = fb; + } + } + + if ((frame_bytes && i + frame_and_padding <= mp3_bytes && + drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + (!i && frame_and_padding == mp3_bytes)) + { + *ptr_frame_bytes = frame_and_padding; + return i; + } + *free_format_bytes = 0; + } + } + *ptr_frame_bytes = 0; + return i; +} + +void drmp3dec_init(drmp3dec *dec) +{ + dec->header[0] = 0; +} + +int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes, short *pcm, drmp3dec_frame_info *info) +{ + int i = 0, igr, frame_size = 0, success = 1; + const drmp3_uint8 *hdr; + drmp3_bs bs_frame[1]; + drmp3dec_scratch scratch; + + if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) + { + frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) + { + frame_size = 0; + } + } + if (!frame_size) + { + memset(dec, 0, sizeof(drmp3dec)); + i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + if (!frame_size || i + frame_size > mp3_bytes) + { + info->frame_bytes = i; + return 0; + } + } + + hdr = mp3 + i; + memcpy(dec->header, hdr, DRMP3_HDR_SIZE); + info->frame_bytes = i + frame_size; + info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = drmp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); + + drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); + if (DRMP3_HDR_IS_CRC(hdr)) + { + drmp3_bs_get_bits(bs_frame, 16); + } + + if (info->layer == 3) + { + int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + if (success) + { + for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm += 576*info->channels) + { + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, pcm, scratch.syn[0]); + } + } + drmp3_L3_save_reservoir(dec, &scratch); + } else + { +#ifdef DR_MP3_ONLY_MP3 + return 0; +#else + drmp3_L12_scale_info sci[1]; + drmp3_L12_read_scale_info(hdr, bs_frame, sci); + + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + for (i = 0, igr = 0; igr < 3; igr++) + { + if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + { + i = 0; + drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, pcm, scratch.syn[0]); + memset(scratch.grbuf[0], 0, 576*2*sizeof(float)); + pcm += 384*info->channels; + } + if (bs_frame->pos > bs_frame->limit) + { + drmp3dec_init(dec); + return 0; + } + } +#endif + } + return success*drmp3_hdr_frame_samples(dec->header); +} + + + + +/////////////////////////////////////////////////////////////////////////////// +// +// Main Public API +// +/////////////////////////////////////////////////////////////////////////////// + +// Options. +#ifndef DR_MP3_DEFAULT_CHANNELS +#define DR_MP3_DEFAULT_CHANNELS 2 +#endif +#ifndef DR_MP3_DEFAULT_SAMPLE_RATE +#define DR_MP3_DEFAULT_SAMPLE_RATE 44100 +#endif + + +// Standard library stuff. +#ifndef DRMP3_ASSERT +#include +#define DRMP3_ASSERT(expression) assert(expression) +#endif +#ifndef DRMP3_COPY_MEMORY +#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#endif +#ifndef DRMP3_ZERO_MEMORY +#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#endif +#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef DRMP3_MALLOC +#define DRMP3_MALLOC(sz) malloc((sz)) +#endif +#ifndef DRMP3_REALLOC +#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) +#endif +#ifndef DRMP3_FREE +#define DRMP3_FREE(p) free((p)) +#endif + +#define drmp3_assert DRMP3_ASSERT +#define drmp3_copy_memory DRMP3_COPY_MEMORY +#define drmp3_zero_memory DRMP3_ZERO_MEMORY +#define drmp3_zero_object DRMP3_ZERO_OBJECT +#define drmp3_malloc DRMP3_MALLOC +#define drmp3_realloc DRMP3_REALLOC + +#define drmp3_countof(x) (sizeof(x) / sizeof(x[0])) +#define drmp3_max(x, y) (((x) > (y)) ? (x) : (y)) +#define drmp3_min(x, y) (((x) < (y)) ? (x) : (y)) + +#define DRMP3_DATA_CHUNK_SIZE 16384 // The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends 16K. + +static inline float drmp3_mix_f32(float x, float y, float a) +{ + return x*(1-a) + y*a; +} + +static void drmp3_blend_f32(float* pOut, float* pInA, float* pInB, float factor, drmp3_uint32 channels) +{ + for (drmp3_uint32 i = 0; i < channels; ++i) { + pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor); + } +} + +void drmp3_src_cache_init(drmp3_src* pSRC, drmp3_src_cache* pCache) +{ + drmp3_assert(pSRC != NULL); + drmp3_assert(pCache != NULL); + + pCache->pSRC = pSRC; + pCache->cachedFrameCount = 0; + pCache->iNextFrame = 0; +} + +drmp3_uint64 drmp3_src_cache_read_frames(drmp3_src_cache* pCache, drmp3_uint64 frameCount, float* pFramesOut) +{ + drmp3_assert(pCache != NULL); + drmp3_assert(pCache->pSRC != NULL); + drmp3_assert(pCache->pSRC->onRead != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); + + drmp3_uint32 channels = pCache->pSRC->config.channels; + + drmp3_uint64 totalFramesRead = 0; + while (frameCount > 0) { + // If there's anything in memory go ahead and copy that over first. + drmp3_uint64 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame; + drmp3_uint64 framesToReadFromMemory = frameCount; + if (framesToReadFromMemory > framesRemainingInMemory) { + framesToReadFromMemory = framesRemainingInMemory; + } + + drmp3_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, (drmp3_uint32)(framesToReadFromMemory * channels * sizeof(float))); + pCache->iNextFrame += (drmp3_uint32)framesToReadFromMemory; + + totalFramesRead += framesToReadFromMemory; + frameCount -= framesToReadFromMemory; + if (frameCount == 0) { + break; + } + + + // At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data. + drmp3_assert(frameCount > 0); + pFramesOut += framesToReadFromMemory * channels; + + pCache->iNextFrame = 0; + pCache->cachedFrameCount = 0; + + drmp3_uint32 framesToReadFromClient = drmp3_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels; + if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) { + framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames; + } + + pCache->cachedFrameCount = (drmp3_uint32)pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData); + + + // Get out of this loop if nothing was able to be retrieved. + if (pCache->cachedFrameCount == 0) { + break; + } + } + + return totalFramesRead; +} + + +drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush); +drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush); + +drmp3_bool32 drmp3_src_init(const drmp3_src_config* pConfig, drmp3_src_read_proc onRead, void* pUserData, drmp3_src* pSRC) +{ + if (pSRC == NULL) return DRMP3_FALSE; + drmp3_zero_object(pSRC); + + if (pConfig == NULL || onRead == NULL) return DRMP3_FALSE; + if (pConfig->channels == 0 || pConfig->channels > 2) return DRMP3_FALSE; + + drmp3_copy_memory(&pSRC->config, pConfig, sizeof (drmp3_src_config)); + pSRC->onRead = onRead; + pSRC->pUserData = pUserData; + + if (pSRC->config.cacheSizeInFrames > DRMP3_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) { + pSRC->config.cacheSizeInFrames = DRMP3_SRC_CACHE_SIZE_IN_FRAMES; + } + + drmp3_src_cache_init(pSRC, &pSRC->cache); + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateIn) +{ + if (pSRC == NULL) return DRMP3_FALSE; + + // Must have a sample rate of > 0. + if (sampleRateIn == 0) { + return DRMP3_FALSE; + } + + pSRC->config.sampleRateIn = sampleRateIn; + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_src_set_output_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateOut) +{ + if (pSRC == NULL) return DRMP3_FALSE; + + // Must have a sample rate of > 0. + if (sampleRateOut == 0) { + return DRMP3_FALSE; + } + + pSRC->config.sampleRateOut = sampleRateOut; + return DRMP3_TRUE; +} + +drmp3_uint64 drmp3_src_read_frames_ex(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) +{ + if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0; + + drmp3_src_algorithm algorithm = pSRC->config.algorithm; + + // Always use passthrough if the sample rates are the same. + if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) { + algorithm = drmp3_src_algorithm_none; + } + + // Could just use a function pointer instead of a switch for this... + switch (algorithm) + { + case drmp3_src_algorithm_none: return drmp3_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush); + case drmp3_src_algorithm_linear: return drmp3_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush); + default: return 0; + } +} + +drmp3_uint64 drmp3_src_read_frames(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut) +{ + return drmp3_src_read_frames_ex(pSRC, frameCount, pFramesOut, DRMP3_FALSE); +} + +drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) +{ + drmp3_assert(pSRC != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); + + (void)flush; // Passthrough need not care about flushing. + return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData); +} + +drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush) +{ + drmp3_assert(pSRC != NULL); + drmp3_assert(frameCount > 0); + drmp3_assert(pFramesOut != NULL); + + // For linear SRC, the bin is only 2 frames: 1 prior, 1 future. + + // Load the bin if necessary. + if (!pSRC->algo.linear.isPrevFramesLoaded) { + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin); + if (framesRead == 0) { + return 0; + } + pSRC->algo.linear.isPrevFramesLoaded = DRMP3_TRUE; + } + if (!pSRC->algo.linear.isNextFramesLoaded) { + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels); + if (framesRead == 0) { + return 0; + } + pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE; + } + + float factor = (float)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut; + + drmp3_uint64 totalFramesRead = 0; + while (frameCount > 0) { + // The bin is where the previous and next frames are located. + float* pPrevFrame = pSRC->bin; + float* pNextFrame = pSRC->bin + pSRC->config.channels; + + drmp3_blend_f32((float*)pFramesOut, pPrevFrame, pNextFrame, pSRC->algo.linear.alpha, pSRC->config.channels); + + pSRC->algo.linear.alpha += factor; + + // The new alpha value is how we determine whether or not we need to read fresh frames. + drmp3_uint32 framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha; + pSRC->algo.linear.alpha = pSRC->algo.linear.alpha - framesToReadFromClient; + + for (drmp3_uint32 i = 0; i < framesToReadFromClient; ++i) { + for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) { + pPrevFrame[j] = pNextFrame[j]; + } + + drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame); + if (framesRead == 0) { + for (drmp3_uint32 j = 0; j < pSRC->config.channels; ++j) { + pNextFrame[j] = 0; + } + + if (pSRC->algo.linear.isNextFramesLoaded) { + pSRC->algo.linear.isNextFramesLoaded = DRMP3_FALSE; + } else { + if (flush) { + pSRC->algo.linear.isPrevFramesLoaded = DRMP3_FALSE; + } + } + + break; + } + } + + pFramesOut = (drmp3_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float)); + frameCount -= 1; + totalFramesRead += 1; + + // If there's no frames available we need to get out of this loop. + if (!pSRC->algo.linear.isNextFramesLoaded && (!flush || !pSRC->algo.linear.isPrevFramesLoaded)) { + break; + } + } + + return totalFramesRead; +} + + + +static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3) +{ + drmp3_assert(pMP3 != NULL); + drmp3_assert(pMP3->onRead != NULL); + + if (pMP3->atEnd) { + return DRMP3_FALSE; + } + + do + { + // minimp3 recommends doing data submission in 16K chunks. If we don't have at least 16K bytes available, get more. + if (pMP3->dataSize < DRMP3_DATA_CHUNK_SIZE) { + if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { + pMP3->dataCapacity = DRMP3_DATA_CHUNK_SIZE; + drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity); + if (pNewData == NULL) { + return DRMP3_FALSE; // Out of memory. + } + + pMP3->pData = pNewData; + } + + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; // No data. + } + + pMP3->dataSize += bytesRead; + } + + if (pMP3->dataSize > INT_MAX) { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; // File too big. + } + + drmp3dec_frame_info info; + drmp3_uint32 samplesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pMP3->frames, &info); // <-- Safe size_t -> int conversion thanks to the check above. + if (samplesRead != 0) { + size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes); + for (size_t i = 0; i < leftoverDataSize; ++i) { + pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes]; + } + + pMP3->dataSize = leftoverDataSize; + pMP3->framesConsumed = 0; + pMP3->framesRemaining = samplesRead; + pMP3->frameChannels = info.channels; + pMP3->frameSampleRate = info.hz; + drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->frameSampleRate); + break; + } else { + // Need more data. minimp3 recommends doing data submission in 16K chunks. + if (pMP3->dataCapacity == pMP3->dataSize) { + // No room. Expand. + pMP3->dataCapacity += DRMP3_DATA_CHUNK_SIZE; + drmp3_uint8* pNewData = (drmp3_uint8*)drmp3_realloc(pMP3->pData, pMP3->dataCapacity); + if (pNewData == NULL) { + return DRMP3_FALSE; // Out of memory. + } + + pMP3->pData = pNewData; + } + + // Fill in a chunk. + size_t bytesRead = pMP3->onRead(pMP3->pUserData, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + if (bytesRead == 0) { + pMP3->atEnd = DRMP3_TRUE; + return DRMP3_FALSE; // Error reading more data. + } + + pMP3->dataSize += bytesRead; + } + } while (DRMP3_TRUE); + + return DRMP3_TRUE; +} + +static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData) +{ + drmp3* pMP3 = (drmp3*)pUserData; + drmp3_assert(pMP3 != NULL); + drmp3_assert(pMP3->onRead != NULL); + + float* pFramesOutF = (float*)pFramesOut; + drmp3_uint32 totalFramesRead = 0; + + while (frameCount > 0) { + // Read from the in-memory buffer first. + while (pMP3->framesRemaining > 0 && frameCount > 0) { + if (pMP3->frameChannels == 1) { + if (pMP3->channels == 1) { + // Mono -> Mono. + pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + } else { + // Mono -> Stereo. + pFramesOutF[0] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + pFramesOutF[1] = pMP3->frames[pMP3->framesConsumed] / 32768.0f; + } + } else { + if (pMP3->channels == 1) { + // Stereo -> Mono + float sample = 0; + sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + sample += pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + pFramesOutF[0] = sample * 0.5f; + } else { + // Stereo -> Stereo + pFramesOutF[0] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f; + pFramesOutF[1] = pMP3->frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f; + } + } + + pMP3->framesConsumed += 1; + pMP3->framesRemaining -= 1; + frameCount -= 1; + totalFramesRead += 1; + pFramesOutF += pSRC->config.channels; + } + + if (frameCount == 0) { + break; + } + + drmp3_assert(pMP3->framesRemaining == 0); + + // At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed + // at this point which means we'll also need to update our sample rate conversion pipeline. + if (!drmp3_decode_next_frame(pMP3)) { + break; + } + } + + return totalFramesRead; +} + +drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig) +{ + drmp3_assert(pMP3 != NULL); + drmp3_assert(onRead != NULL); + + // This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. + drmp3dec_init(&pMP3->decoder); + + // The config can be null in which case we use defaults. + drmp3_config config; + if (pConfig != NULL) { + drmp3_copy_memory(&config, pConfig, sizeof (drmp3_config)); + } else { + drmp3_zero_object(&config); + } + + pMP3->channels = config.outputChannels; + if (pMP3->channels == 0) { + pMP3->channels = DR_MP3_DEFAULT_CHANNELS; + } + + // Cannot have more than 2 channels. + if (pMP3->channels > 2) { + pMP3->channels = 2; + } + + pMP3->sampleRate = config.outputSampleRate; + if (pMP3->sampleRate == 0) { + pMP3->sampleRate = DR_MP3_DEFAULT_SAMPLE_RATE; + } + + pMP3->onRead = onRead; + pMP3->onSeek = onSeek; + pMP3->pUserData = pUserData; + + // We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate. + drmp3_src_config srcConfig; + drmp3_zero_object(&srcConfig); + srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE; + srcConfig.sampleRateOut = pMP3->sampleRate; + srcConfig.channels = pMP3->channels; + srcConfig.algorithm = drmp3_src_algorithm_linear; + if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) { + return DRMP3_FALSE; + } + + // Decode the first frame to confirm that it is indeed a valid MP3 stream. + if (!drmp3_decode_next_frame(pMP3)) { + return DRMP3_FALSE; // Not a valid MP3 stream. + } + + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig) +{ + if (pMP3 == NULL || onRead == NULL) { + return DRMP3_FALSE; + } + + drmp3_zero_object(pMP3); + return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pConfig); +} + + +static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + drmp3* pMP3 = (drmp3*)pUserData; + drmp3_assert(pMP3 != NULL); + drmp3_assert(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + + size_t bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + drmp3_copy_memory(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + pMP3->memory.currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +{ + drmp3* pMP3 = (drmp3*)pUserData; + drmp3_assert(pMP3 != NULL); + + if (origin == drmp3_seek_origin_current) { + if (byteOffset > 0) { + if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { + byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); // Trying to seek too far forward. + } + } else { + if (pMP3->memory.currentReadPos < (size_t)-byteOffset) { + byteOffset = -(int)pMP3->memory.currentReadPos; // Trying to seek too far backwards. + } + } + + // This will never underflow thanks to the clamps above. + pMP3->memory.currentReadPos += byteOffset; + } else { + if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { + pMP3->memory.currentReadPos = byteOffset; + } else { + pMP3->memory.currentReadPos = pMP3->memory.dataSize; // Trying to seek too far forward. + } + } + + return DRMP3_TRUE; +} + +drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_config* pConfig) +{ + if (pMP3 == NULL) { + return DRMP3_FALSE; + } + + drmp3_zero_object(pMP3); + + if (pData == NULL || dataSize == 0) { + return DRMP3_FALSE; + } + + pMP3->memory.pData = (const drmp3_uint8*)pData; + pMP3->memory.dataSize = dataSize; + pMP3->memory.currentReadPos = 0; + + return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pConfig); +} + + +#ifndef DR_MP3_NO_STDIO +#include + +static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +{ + return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) +{ + return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig) +{ + FILE* pFile; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (fopen_s(&pFile, filePath, "rb") != 0) { + return DRMP3_FALSE; + } +#else + pFile = fopen(filePath, "rb"); + if (pFile == NULL) { + return DRMP3_FALSE; + } +#endif + + return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pConfig); +} +#endif + +void drmp3_uninit(drmp3* pMP3) +{ + if (pMP3 == NULL) return; + +#ifndef DR_MP3_NO_STDIO + if (pMP3->onRead == drmp3__on_read_stdio) { + fclose((FILE*)pMP3->pUserData); + } +#endif + + drmp3_free(pMP3->pData); +} + +drmp3_uint64 drmp3_read_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) +{ + if (pMP3 == NULL || pMP3->onRead == NULL) return 0; + + drmp3_uint64 totalFramesRead = 0; + + if (pBufferOut == NULL) { + float temp[4096]; + while (framesToRead > 0) { + drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels; + if (framesToReadRightNow > framesToRead) { + framesToReadRightNow = framesToRead; + } + + drmp3_uint64 framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + + framesToRead -= framesJustRead; + totalFramesRead += framesJustRead; + } + } else { + totalFramesRead = drmp3_src_read_frames_ex(&pMP3->src, framesToRead, pBufferOut, DRMP3_TRUE); + } + + return totalFramesRead; +} + +drmp3_bool32 drmp3_seek_to_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +{ + if (pMP3 == NULL || pMP3->onSeek == NULL) return DRMP3_FALSE; + + // Seek to the start of the stream to begin with. + if (!pMP3->onSeek(pMP3->pUserData, 0, drmp3_seek_origin_start)) { + return DRMP3_FALSE; + } + + // Clear any cached data. + pMP3->framesConsumed = 0; + pMP3->framesRemaining = 0; + pMP3->dataSize = 0; + pMP3->atEnd = DRMP3_FALSE; + + // TODO: Optimize. + // + // This is inefficient. We simply read frames from the start of the stream. + drmp3_uint64 framesRead = drmp3_read_f32(pMP3, frameIndex, NULL); + if (framesRead != frameIndex) { + return DRMP3_FALSE; + } + + return DRMP3_TRUE; +} + + + +float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3_assert(pMP3 != NULL); + + drmp3_uint64 totalFramesRead = 0; + drmp3_uint64 framesCapacity = 0; + float* pFrames = NULL; + + float temp[4096]; + for (;;) { + drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels; + drmp3_uint64 framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp); + if (framesJustRead == 0) { + break; + } + + // Reallocate the output buffer if there's not enough room. + if (framesCapacity < totalFramesRead + framesJustRead) { + framesCapacity *= 2; + if (framesCapacity < totalFramesRead + framesJustRead) { + framesCapacity = totalFramesRead + framesJustRead; + } + + drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float); + if (newFramesBufferSize > SIZE_MAX) { + break; + } + + float* pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize); + if (pNewFrames == NULL) { + drmp3_free(pFrames); + break; + } + + pFrames = pNewFrames; + } + + drmp3_copy_memory(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + totalFramesRead += framesJustRead; + + // If the number of frames we asked for is less that what we actually read it means we've reached the end. + if (framesJustRead != framesToReadRightNow) { + break; + } + } + + if (pConfig != NULL) { + pConfig->outputChannels = pMP3->channels; + pConfig->outputSampleRate = pMP3->sampleRate; + } + + drmp3_uninit(pMP3); + + if (pTotalFrameCount) *pTotalFrameCount = totalFramesRead; + return pFrames; +} + +float* drmp3_open_and_decode_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3 mp3; + if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig)) { + return NULL; + } + + return drmp3__full_decode_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +float* drmp3_open_and_decode_memory_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3 mp3; + if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig)) { + return NULL; + } + + return drmp3__full_decode_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} + +#ifndef DR_MP3_NO_STDIO +float* drmp3_open_and_decode_file_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +{ + drmp3 mp3; + if (!drmp3_init_file(&mp3, filePath, pConfig)) { + return NULL; + } + + return drmp3__full_decode_and_close_f32(&mp3, pConfig, pTotalFrameCount); +} +#endif + +void drmp3_free(void* p) +{ + DRMP3_FREE(p); +} + +#endif /*DR_MP3_IMPLEMENTATION*/ + + +// DIFFERENCES BETWEEN minimp3 AND dr_mp3 +// ====================================== +// - First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the +// code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes. +// - dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data +// to the decoder, the decoder _pulls_ data from your callbacks. +// - In addition to callbacks, a decoder can be initialized from a block of memory and a file. +// - The dr_mp3 pull API reads PCM frames rather than whole MP3 frames. +// - dr_mp3 adds convenience APIs for opening and decoding entire files in one go. +// - dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects +// as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when +// using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this. + + +// REVISION HISTORY +// =============== +// +// v0.2.7 - 2018-07-13 +// - Bring up to date with minimp3. +// +// v0.2.6 - 2018-07-12 +// - Bring up to date with minimp3. +// +// v0.2.5 - 2018-06-22 +// - Bring up to date with minimp3. +// +// v0.2.4 - 2018-05-12 +// - Bring up to date with minimp3. +// +// v0.2.3 - 2018-04-29 +// - Fix TCC build. +// +// v0.2.2 - 2018-04-28 +// - Fix bug when opening a decoder from memory. +// +// v0.2.1 - 2018-04-27 +// - Efficiency improvements when the decoder reaches the end of the stream. +// +// v0.2 - 2018-04-21 +// - Bring up to date with minimp3. +// - Start using major.minor.revision versioning. +// +// v0.1d - 2018-03-30 +// - Bring up to date with minimp3. +// +// v0.1c - 2018-03-11 +// - Fix C++ build error. +// +// v0.1b - 2018-03-07 +// - Bring up to date with minimp3. +// +// v0.1a - 2018-02-28 +// - Fix compilation error on GCC/Clang. +// - Fix some warnings. +// +// v0.1 - 2018-02-xx +// - Initial versioned release. + + +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ + +/* + https://github.com/lieff/minimp3 + To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. + This software is distributed without any warranty. + See . +*/ diff --git a/SDL2_sound/meson.build b/SDL2_sound/meson.build new file mode 100644 index 00000000..1e94fadb --- /dev/null +++ b/SDL2_sound/meson.build @@ -0,0 +1,22 @@ +global_include_dirs += include_directories('.') +global_dependencies += dependency('AudioToolbox') + +global_sources += files( + 'dr_flac.h', + 'dr_mp3.h', + 'SDL_sound_aiff.c', + 'SDL_sound_au.c', + 'SDL_sound_coreaudio.c', + 'SDL_sound_flac.c', + 'SDL_sound_internal.h', + 'SDL_sound_modplug.c', + 'SDL_sound_mp3.c', + 'SDL_sound_raw.c', + 'SDL_sound_shn.c', + 'SDL_sound_voc.c', + 'SDL_sound_vorbis.c', + 'SDL_sound_wav.c', + 'SDL_sound.c', + 'SDL_sound.h', + 'stb_vorbis.h' +) \ No newline at end of file diff --git a/SDL2_sound/stb_vorbis.h b/SDL2_sound/stb_vorbis.h new file mode 100644 index 00000000..d7ea8341 --- /dev/null +++ b/SDL2_sound/stb_vorbis.h @@ -0,0 +1,5567 @@ +// Ogg Vorbis audio decoder - v1.14 - public domain +// http://nothings.org/stb_vorbis/ +// +// Original version written by Sean Barrett in 2007. +// +// Originally sponsored by RAD Game Tools. Seeking implementation +// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker, +// Elias Software, Aras Pranckevicius, and Sean Barrett. +// +// LICENSE +// +// See end of file for license information. +// +// Limitations: +// +// - floor 0 not supported (used in old ogg vorbis files pre-2004) +// - lossless sample-truncation at beginning ignored +// - cannot concatenate multiple vorbis streams +// - sample positions are 32-bit, limiting seekable 192Khz +// files to around 6 hours (Ogg supports 64-bit) +// +// Feature contributors: +// Dougall Johnson (sample-exact seeking) +// +// Bugfix/warning contributors: +// Terje Mathisen Niklas Frykholm Andy Hill +// Casey Muratori John Bolton Gargaj +// Laurent Gomila Marc LeBlanc Ronny Chevalier +// Bernhard Wodo Evan Balster alxprd@github +// Tom Beaumont Ingo Leitgeb Nicolas Guillemot +// Phillip Bennefall Rohit Thiago Goulart +// manxorist@github saga musix github:infatum +// Timur Gagiev +// +// Partial history: +// 1.14 - 2018-02-11 - delete bogus dealloca usage +// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully) +// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files +// 1.11 - 2017-07-23 - fix MinGW compilation +// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory +// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version +// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame +// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const +// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) +// some crash fixes when out of memory or with corrupt files +// fix some inappropriately signed shifts +// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant +// 1.04 - 2014-08-27 - fix missing const-correct case in API +// 1.03 - 2014-08-07 - warning fixes +// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows +// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct) +// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel; +// (API change) report sample rate for decode-full-file funcs +// +// See end of file for full version history. + + +////////////////////////////////////////////////////////////////////////////// +// +// HEADER BEGINS HERE +// + +#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H +#define STB_VORBIS_INCLUDE_STB_VORBIS_H + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) +#define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/////////// THREAD SAFETY + +// Individual stb_vorbis* handles are not thread-safe; you cannot decode from +// them from multiple threads at the same time. However, you can have multiple +// stb_vorbis* handles and decode from them independently in multiple thrads. + + +/////////// MEMORY ALLOCATION + +// normally stb_vorbis uses malloc() to allocate memory at startup, +// and alloca() to allocate temporary memory during a frame on the +// stack. (Memory consumption will depend on the amount of setup +// data in the file and how you set the compile flags for speed +// vs. size. In my test files the maximal-size usage is ~150KB.) +// +// You can modify the wrapper functions in the source (setup_malloc, +// setup_temp_malloc, temp_malloc) to change this behavior, or you +// can use a simpler allocation model: you pass in a buffer from +// which stb_vorbis will allocate _all_ its memory (including the +// temp memory). "open" may fail with a VORBIS_outofmem if you +// do not pass in enough data; there is no way to determine how +// much you do need except to succeed (at which point you can +// query get_info to find the exact amount required. yes I know +// this is lame). +// +// If you pass in a non-NULL buffer of the type below, allocation +// will occur from it as described above. Otherwise just pass NULL +// to use malloc()/alloca() + +typedef struct +{ + char *alloc_buffer; + int alloc_buffer_length_in_bytes; +} stb_vorbis_alloc; + + +/////////// FUNCTIONS USEABLE WITH ALL INPUT MODES + +typedef struct stb_vorbis stb_vorbis; + +typedef struct +{ + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int setup_temp_memory_required; + unsigned int temp_memory_required; + + int max_frame_size; +} stb_vorbis_info; + +// get general information about the file +extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f); + +// get the last error detected (clears it, too) +extern int stb_vorbis_get_error(stb_vorbis *f); + +// close an ogg vorbis file and free all memory in use +extern void stb_vorbis_close(stb_vorbis *f); + +// this function returns the offset (in samples) from the beginning of the +// file that will be returned by the next decode, if it is known, or -1 +// otherwise. after a flush_pushdata() call, this may take a while before +// it becomes valid again. +// NOT WORKING YET after a seek with PULLDATA API +extern int stb_vorbis_get_sample_offset(stb_vorbis *f); + +// returns the current seek point within the file, or offset from the beginning +// of the memory buffer. In pushdata mode it returns 0. +extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f); + +/////////// PUSHDATA API + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +// this API allows you to get blocks of data from any source and hand +// them to stb_vorbis. you have to buffer them; stb_vorbis will tell +// you how much it used, and you have to give it the rest next time; +// and stb_vorbis may not have enough data to work with and you will +// need to give it the same data again PLUS more. Note that the Vorbis +// specification does not bound the size of an individual frame. + +extern stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char * datablock, int datablock_length_in_bytes, + int *datablock_memory_consumed_in_bytes, + int *error, + const stb_vorbis_alloc *alloc_buffer); +// create a vorbis decoder by passing in the initial data block containing +// the ogg&vorbis headers (you don't need to do parse them, just provide +// the first N bytes of the file--you're told if it's not enough, see below) +// on success, returns an stb_vorbis *, does not set error, returns the amount of +// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes; +// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed +// if returns NULL and *error is VORBIS_need_more_data, then the input block was +// incomplete and you need to pass in a larger block from the start of the file + +extern int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, + const unsigned char *datablock, int datablock_length_in_bytes, + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ); +// decode a frame of audio sample data if possible from the passed-in data block +// +// return value: number of bytes we used from datablock +// +// possible cases: +// 0 bytes used, 0 samples output (need more data) +// N bytes used, 0 samples output (resynching the stream, keep going) +// N bytes used, M samples output (one frame of data) +// note that after opening a file, you will ALWAYS get one N-bytes,0-sample +// frame, because Vorbis always "discards" the first frame. +// +// Note that on resynch, stb_vorbis will rarely consume all of the buffer, +// instead only datablock_length_in_bytes-3 or less. This is because it wants +// to avoid missing parts of a page header if they cross a datablock boundary, +// without writing state-machiney code to record a partial detection. +// +// The number of channels returned are stored in *channels (which can be +// NULL--it is always the same as the number of channels reported by +// get_info). *output will contain an array of float* buffers, one per +// channel. In other words, (*output)[0][0] contains the first sample from +// the first channel, and (*output)[1][0] contains the first sample from +// the second channel. + +extern void stb_vorbis_flush_pushdata(stb_vorbis *f); +// inform stb_vorbis that your next datablock will not be contiguous with +// previous ones (e.g. you've seeked in the data); future attempts to decode +// frames will cause stb_vorbis to resynchronize (as noted above), and +// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it +// will begin decoding the _next_ frame. +// +// if you want to seek using pushdata, you need to seek in your file, then +// call stb_vorbis_flush_pushdata(), then start calling decoding, then once +// decoding is returning you data, call stb_vorbis_get_sample_offset, and +// if you don't like the result, seek your file again and repeat. +#endif + + +////////// PULLING INPUT API + +#ifndef STB_VORBIS_NO_PULLDATA_API +// This API assumes stb_vorbis is allowed to pull data from a source-- +// either a block of memory containing the _entire_ vorbis stream, or a +// FILE * that you or it create, or possibly some other reading mechanism +// if you go modify the source to replace the FILE * case with some kind +// of callback to your code. (But if you don't support seeking, you may +// just want to go ahead and use pushdata.) + +#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output); +#endif +#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION) +extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output); +#endif +// decode an entire file and output the data interleaved into a malloc()ed +// buffer stored in *output. The return value is the number of samples +// decoded, or -1 if the file could not be opened or was not an ogg vorbis file. +// When you're done with it, just free() the pointer returned in *output. + +extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an ogg vorbis stream in memory (note +// this must be the entire stream!). on failure, returns NULL and sets *error + +#ifndef STB_VORBIS_NO_STDIO +extern stb_vorbis * stb_vorbis_open_filename(const char *filename, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from a filename via fopen(). on failure, +// returns NULL and sets *error (possibly to VORBIS_file_open_failure). + +extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell). on failure, returns NULL and sets *error. +// note that stb_vorbis must "own" this stream; if you seek it in between +// calls to stb_vorbis, it will become confused. Morever, if you attempt to +// perform stb_vorbis_seek_*() operations on this file, it will assume it +// owns the _entire_ rest of the file after the start point. Use the next +// function, stb_vorbis_open_file_section(), to limit it. + +extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, + int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len); +// create an ogg vorbis decoder from an open FILE *, looking for a stream at +// the _current_ seek point (ftell); the stream will be of length 'len' bytes. +// on failure, returns NULL and sets *error. note that stb_vorbis must "own" +// this stream; if you seek it in between calls to stb_vorbis, it will become +// confused. +#endif + +#ifdef __SDL_SOUND_INTERNAL__ +extern stb_vorbis * stb_vorbis_open_rwops_section(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length); +extern stb_vorbis * stb_vorbis_open_rwops(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc); +#endif + +extern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number); +extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number); +// these functions seek in the Vorbis file to (approximately) 'sample_number'. +// after calling seek_frame(), the next call to get_frame_*() will include +// the specified sample. after calling stb_vorbis_seek(), the next call to +// stb_vorbis_get_samples_* will start with the specified sample. If you +// do not need to seek to EXACTLY the target sample when using get_samples_*, +// you can also use seek_frame(). + +extern int stb_vorbis_seek_start(stb_vorbis *f); +// this function is equivalent to stb_vorbis_seek(f,0) + +extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f); +extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f); +// these functions return the total length of the vorbis stream + +extern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output); +// decode the next frame and return the number of samples. the number of +// channels returned are stored in *channels (which can be NULL--it is always +// the same as the number of channels reported by get_info). *output will +// contain an array of float* buffers, one per channel. These outputs will +// be overwritten on the next call to stb_vorbis_get_frame_*. +// +// You generally should not intermix calls to stb_vorbis_get_frame_*() +// and stb_vorbis_get_samples_*(), since the latter calls the former. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts); +extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples); +#endif +// decode the next frame and return the number of *samples* per channel. +// Note that for interleaved data, you pass in the number of shorts (the +// size of your array), but the return value is the number of samples per +// channel, not the total number of samples. +// +// The data is coerced to the number of channels you request according to the +// channel coercion rules (see below). You must pass in the size of your +// buffer(s) so that stb_vorbis will not overwrite the end of the buffer. +// The maximum buffer size needed can be gotten from get_info(); however, +// the Vorbis I specification implies an absolute maximum of 4096 samples +// per channel. + +// Channel coercion rules: +// Let M be the number of channels requested, and N the number of channels present, +// and Cn be the nth channel; let stereo L be the sum of all L and center channels, +// and stereo R be the sum of all R and center channels (channel assignment from the +// vorbis spec). +// M N output +// 1 k sum(Ck) for all k +// 2 * stereo L, stereo R +// k l k > l, the first l channels, then 0s +// k l k <= l, the first k channels +// Note that this is not _good_ surround etc. mixing at all! It's just so +// you get something useful. + +extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats); +extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples); +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES. +// Returns the number of samples stored per channel; it may be less than requested +// at the end of the file. If there are no more samples in the file, returns 0. + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +extern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts); +extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples); +#endif +// gets num_samples samples, not necessarily on a frame boundary--this requires +// buffering so you have to supply the buffers. Applies the coercion rules above +// to produce 'channels' channels. Returns the number of samples stored per channel; +// it may be less than requested at the end of the file. If there are no more +// samples in the file, returns 0. + +#endif + +//////// ERROR CODES + +enum STBVorbisError +{ + VORBIS__no_error, + + VORBIS_need_more_data=1, // not a real error + + VORBIS_invalid_api_mixing, // can't mix API modes + VORBIS_outofmem, // not enough memory + VORBIS_feature_not_supported, // uses floor 0 + VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small + VORBIS_file_open_failure, // fopen() failed + VORBIS_seek_without_length, // can't seek in unknown-length file + + VORBIS_unexpected_eof=10, // file is truncated? + VORBIS_seek_invalid, // seek past EOF + + // decoding errors (corrupt/invalid stream) -- you probably + // don't care about the exact details of these + + // vorbis errors: + VORBIS_invalid_setup=20, + VORBIS_invalid_stream, + + // ogg errors: + VORBIS_missing_capture_pattern=30, + VORBIS_invalid_stream_structure_version, + VORBIS_continued_packet_flag_invalid, + VORBIS_incorrect_stream_serial_number, + VORBIS_invalid_first_page, + VORBIS_bad_packet_type, + VORBIS_cant_find_last_page, + VORBIS_seek_failed +}; + + +#ifdef __cplusplus +} +#endif + +#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H +// +// HEADER ENDS HERE +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef STB_VORBIS_HEADER_ONLY + +// global configuration settings (e.g. set these in the project/makefile), +// or just set them in this file at the top (although ideally the first few +// should be visible when the header file is compiled too, although it's not +// crucial) + +// STB_VORBIS_NO_PUSHDATA_API +// does not compile the code for the various stb_vorbis_*_pushdata() +// functions +// #define STB_VORBIS_NO_PUSHDATA_API + +// STB_VORBIS_NO_PULLDATA_API +// does not compile the code for the non-pushdata APIs +// #define STB_VORBIS_NO_PULLDATA_API + +// STB_VORBIS_NO_STDIO +// does not compile the code for the APIs that use FILE *s internally +// or externally (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_STDIO + +// STB_VORBIS_NO_INTEGER_CONVERSION +// does not compile the code for converting audio sample data from +// float to integer (implied by STB_VORBIS_NO_PULLDATA_API) +// #define STB_VORBIS_NO_INTEGER_CONVERSION + +// STB_VORBIS_NO_FAST_SCALED_FLOAT +// does not use a fast float-to-int trick to accelerate float-to-int on +// most platforms which requires endianness be defined correctly. +//#define STB_VORBIS_NO_FAST_SCALED_FLOAT + + +// STB_VORBIS_MAX_CHANNELS [number] +// globally define this to the maximum number of channels you need. +// The spec does not put a restriction on channels except that +// the count is stored in a byte, so 255 is the hard limit. +// Reducing this saves about 16 bytes per value, so using 16 saves +// (255-16)*16 or around 4KB. Plus anything other memory usage +// I forgot to account for. Can probably go as low as 8 (7.1 audio), +// 6 (5.1 audio), or 2 (stereo only). +#ifndef STB_VORBIS_MAX_CHANNELS +#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone? +#endif + +// STB_VORBIS_PUSHDATA_CRC_COUNT [number] +// after a flush_pushdata(), stb_vorbis begins scanning for the +// next valid page, without backtracking. when it finds something +// that looks like a page, it streams through it and verifies its +// CRC32. Should that validation fail, it keeps scanning. But it's +// possible that _while_ streaming through to check the CRC32 of +// one candidate page, it sees another candidate page. This #define +// determines how many "overlapping" candidate pages it can search +// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas +// garbage pages could be as big as 64KB, but probably average ~16KB. +// So don't hose ourselves by scanning an apparent 64KB page and +// missing a ton of real ones in the interim; so minimum of 2 +#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT +#define STB_VORBIS_PUSHDATA_CRC_COUNT 4 +#endif + +// STB_VORBIS_FAST_HUFFMAN_LENGTH [number] +// sets the log size of the huffman-acceleration table. Maximum +// supported value is 24. with larger numbers, more decodings are O(1), +// but the table size is larger so worse cache missing, so you'll have +// to probe (and try multiple ogg vorbis files) to find the sweet spot. +#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH +#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10 +#endif + +// STB_VORBIS_FAST_BINARY_LENGTH [number] +// sets the log size of the binary-search acceleration table. this +// is used in similar fashion to the fast-huffman size to set initial +// parameters for the binary search + +// STB_VORBIS_FAST_HUFFMAN_INT +// The fast huffman tables are much more efficient if they can be +// stored as 16-bit results instead of 32-bit results. This restricts +// the codebooks to having only 65535 possible outcomes, though. +// (At least, accelerated by the huffman table.) +#ifndef STB_VORBIS_FAST_HUFFMAN_INT +#define STB_VORBIS_FAST_HUFFMAN_SHORT +#endif + +// STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH +// If the 'fast huffman' search doesn't succeed, then stb_vorbis falls +// back on binary searching for the correct one. This requires storing +// extra tables with the huffman codes in sorted order. Defining this +// symbol trades off space for speed by forcing a linear search in the +// non-fast case, except for "sparse" codebooks. +// #define STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + +// STB_VORBIS_DIVIDES_IN_RESIDUE +// stb_vorbis precomputes the result of the scalar residue decoding +// that would otherwise require a divide per chunk. you can trade off +// space for time by defining this symbol. +// #define STB_VORBIS_DIVIDES_IN_RESIDUE + +// STB_VORBIS_DIVIDES_IN_CODEBOOK +// vorbis VQ codebooks can be encoded two ways: with every case explicitly +// stored, or with all elements being chosen from a small range of values, +// and all values possible in all elements. By default, stb_vorbis expands +// this latter kind out to look like the former kind for ease of decoding, +// because otherwise an integer divide-per-vector-element is required to +// unpack the index. If you define STB_VORBIS_DIVIDES_IN_CODEBOOK, you can +// trade off storage for speed. +//#define STB_VORBIS_DIVIDES_IN_CODEBOOK + +#ifdef STB_VORBIS_CODEBOOK_SHORTS +#error "STB_VORBIS_CODEBOOK_SHORTS is no longer supported as it produced incorrect results for some input formats" +#endif + +// STB_VORBIS_DIVIDE_TABLE +// this replaces small integer divides in the floor decode loop with +// table lookups. made less than 1% difference, so disabled by default. + +// STB_VORBIS_NO_INLINE_DECODE +// disables the inlining of the scalar codebook fast-huffman decode. +// might save a little codespace; useful for debugging +// #define STB_VORBIS_NO_INLINE_DECODE + +// STB_VORBIS_NO_DEFER_FLOOR +// Normally we only decode the floor without synthesizing the actual +// full curve. We can instead synthesize the curve immediately. This +// requires more memory and is very likely slower, so I don't think +// you'd ever want to do it except for debugging. +// #define STB_VORBIS_NO_DEFER_FLOOR + + + + +////////////////////////////////////////////////////////////////////////////// + +#ifdef STB_VORBIS_NO_PULLDATA_API + #define STB_VORBIS_NO_INTEGER_CONVERSION + #define STB_VORBIS_NO_STDIO +#endif + +#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO) + #define STB_VORBIS_NO_STDIO 1 +#endif + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + + // only need endianness for fast-float-to-int, which we don't + // use for pushdata + + #ifndef STB_VORBIS_BIG_ENDIAN + #define STB_VORBIS_ENDIAN 0 + #else + #define STB_VORBIS_ENDIAN 1 + #endif + +#endif +#endif + + +#ifndef STB_VORBIS_NO_STDIO +#include +#endif + +#ifndef STB_VORBIS_NO_CRT + #include + #include + #include + #include + + // find definition of alloca if it's not in stdlib.h: + #if defined(_MSC_VER) || defined(__MINGW32__) + #include + #endif + #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #include + #endif +#else // STB_VORBIS_NO_CRT + #ifndef NULL + #define NULL 0 + #endif + #ifndef malloc + #define malloc(s) 0 + #endif + #ifndef free + #define free(s) ((void) 0) + #endif + #ifndef realloc + #define realloc(s) 0 + #endif +#endif // STB_VORBIS_NO_CRT + +#include + +#ifdef __MINGW32__ + #define alloca __builtin_alloca +#endif + +#ifndef STB_FORCEINLINE + #if defined(_MSC_VER) + #define STB_FORCEINLINE __forceinline + #elif defined(__GNUC__) || defined(__clang__) + #define STB_FORCEINLINE __attribute__((always_inline)) + #else + #define STB_FORCEINLINE + #endif +#endif + +#if STB_VORBIS_MAX_CHANNELS > 256 +#error "Value of STB_VORBIS_MAX_CHANNELS outside of allowed range" +#endif + +#if STB_VORBIS_FAST_HUFFMAN_LENGTH > 24 +#error "Value of STB_VORBIS_FAST_HUFFMAN_LENGTH outside of allowed range" +#endif + + +#if 0 +#include +#define CHECK(f) _CrtIsValidHeapPointer(f->channel_buffers[1]) +#else +#define CHECK(f) ((void) 0) +#endif + +#define MAX_BLOCKSIZE_LOG 13 // from specification +#define MAX_BLOCKSIZE (1 << MAX_BLOCKSIZE_LOG) + +#ifdef __SDL_SOUND_INTERNAL__ +typedef Uint8 uint8; +typedef Sint8 int8; +typedef Uint16 uint16; +typedef Sint16 int16; +typedef Uint32 uint32; +typedef Sint32 int32; +#else +typedef unsigned char uint8; +typedef signed char int8; +typedef unsigned short uint16; +typedef signed short int16; +typedef unsigned int uint32; +typedef signed int int32; +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +typedef float codetype; + +// @NOTE +// +// Some arrays below are tagged "//varies", which means it's actually +// a variable-sized piece of data, but rather than malloc I assume it's +// small enough it's better to just allocate it all together with the +// main thing +// +// Most of the variables are specified with the smallest size I could pack +// them into. It might give better performance to make them all full-sized +// integers. It should be safe to freely rearrange the structures or change +// the sizes larger--nothing relies on silently truncating etc., nor the +// order of variables. + +#define FAST_HUFFMAN_TABLE_SIZE (1 << STB_VORBIS_FAST_HUFFMAN_LENGTH) +#define FAST_HUFFMAN_TABLE_MASK (FAST_HUFFMAN_TABLE_SIZE - 1) + +typedef struct +{ + int dimensions, entries; + uint8 *codeword_lengths; + float minimum_value; + float delta_value; + uint8 value_bits; + uint8 lookup_type; + uint8 sequence_p; + uint8 sparse; + uint32 lookup_values; + codetype *multiplicands; + uint32 *codewords; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + int16 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #else + int32 fast_huffman[FAST_HUFFMAN_TABLE_SIZE]; + #endif + uint32 *sorted_codewords; + int *sorted_values; + int sorted_entries; +} Codebook; + +typedef struct +{ + uint8 order; + uint16 rate; + uint16 bark_map_size; + uint8 amplitude_bits; + uint8 amplitude_offset; + uint8 number_of_books; + uint8 book_list[16]; // varies +} Floor0; + +typedef struct +{ + uint8 partitions; + uint8 partition_class_list[32]; // varies + uint8 class_dimensions[16]; // varies + uint8 class_subclasses[16]; // varies + uint8 class_masterbooks[16]; // varies + int16 subclass_books[16][8]; // varies + uint16 Xlist[31*8+2]; // varies + uint8 sorted_order[31*8+2]; + uint8 neighbors[31*8+2][2]; + uint8 floor1_multiplier; + uint8 rangebits; + int values; +} Floor1; + +typedef union +{ + Floor0 floor0; + Floor1 floor1; +} Floor; + +typedef struct +{ + uint32 begin, end; + uint32 part_size; + uint8 classifications; + uint8 classbook; + uint8 **classdata; + int16 (*residue_books)[8]; +} Residue; + +typedef struct +{ + uint8 magnitude; + uint8 angle; + uint8 mux; +} MappingChannel; + +typedef struct +{ + uint16 coupling_steps; + MappingChannel *chan; + uint8 submaps; + uint8 submap_floor[15]; // varies + uint8 submap_residue[15]; // varies +} Mapping; + +typedef struct +{ + uint8 blockflag; + uint8 mapping; + uint16 windowtype; + uint16 transformtype; +} Mode; + +typedef struct +{ + uint32 goal_crc; // expected crc if match + int bytes_left; // bytes left in packet + uint32 crc_so_far; // running crc + int bytes_done; // bytes processed in _current_ chunk + uint32 sample_loc; // granule pos encoded in page +} CRCscan; + +typedef struct +{ + uint32 page_start, page_end; + uint32 last_decoded_sample; +} ProbedPage; + +struct stb_vorbis +{ + // user-accessible info + unsigned int sample_rate; + int channels; + + unsigned int setup_memory_required; + unsigned int temp_memory_required; + unsigned int setup_temp_memory_required; + + // input config +#ifndef STB_VORBIS_NO_STDIO + FILE *f; + uint32 f_start; + int close_on_free; +#endif + +#ifdef __SDL_SOUND_INTERNAL__ + SDL_RWops *rwops; + uint32 rwops_start; + int close_on_free; +#endif + + uint8 *stream; + uint8 *stream_start; + uint8 *stream_end; + + uint32 stream_len; + + uint8 push_mode; + + uint32 first_audio_page_offset; + + ProbedPage p_first, p_last; + + // memory management + stb_vorbis_alloc alloc; + int setup_offset; + int temp_offset; + + // run-time results + int eof; + enum STBVorbisError error; + + // user-useful data + + // header info + int blocksize[2]; + int blocksize_0, blocksize_1; + int codebook_count; + Codebook *codebooks; + int floor_count; + uint16 floor_types[64]; // varies + Floor *floor_config; + int residue_count; + uint16 residue_types[64]; // varies + Residue *residue_config; + int mapping_count; + Mapping *mapping; + int mode_count; + Mode mode_config[64]; // varies + + uint32 total_samples; + + // decode buffer + float *channel_buffers[STB_VORBIS_MAX_CHANNELS]; + float *outputs [STB_VORBIS_MAX_CHANNELS]; + + float *previous_window[STB_VORBIS_MAX_CHANNELS]; + int previous_length; + + #ifndef STB_VORBIS_NO_DEFER_FLOOR + int16 *finalY[STB_VORBIS_MAX_CHANNELS]; + #else + float *floor_buffers[STB_VORBIS_MAX_CHANNELS]; + #endif + + uint32 current_loc; // sample location of next frame to decode + int current_loc_valid; + + // per-blocksize precomputed data + + // twiddle factors + float *A[2],*B[2],*C[2]; + float *window[2]; + uint16 *bit_reverse[2]; + + // current page/packet/segment streaming info + uint32 serial; // stream serial number for verification + int last_page; + int segment_count; + uint8 segments[255]; + uint8 page_flag; + uint8 bytes_in_seg; + uint8 first_decode; + int next_seg; + int last_seg; // flag that we're on the last segment + int last_seg_which; // what was the segment number of the last seg? + uint32 acc; + int valid_bits; + int packet_bytes; + int end_seg_with_known_loc; + uint32 known_loc_for_packet; + int discard_samples_deferred; + uint32 samples_output; + + // push mode scanning + int page_crc_tests; // only in push_mode: number of tests active; -1 if not searching +#ifndef STB_VORBIS_NO_PUSHDATA_API + CRCscan scan[STB_VORBIS_PUSHDATA_CRC_COUNT]; +#endif + + // sample-access + int channel_buffer_start; + int channel_buffer_end; +}; + +#if defined(STB_VORBIS_NO_PUSHDATA_API) + #define IS_PUSH_MODE(f) FALSE +#elif defined(STB_VORBIS_NO_PULLDATA_API) + #define IS_PUSH_MODE(f) TRUE +#else + #define IS_PUSH_MODE(f) ((f)->push_mode) +#endif + +typedef struct stb_vorbis vorb; + +static int error(vorb *f, enum STBVorbisError e) +{ + f->error = e; + if (!f->eof && e != VORBIS_need_more_data) { + f->error=e; // breakpoint for debugging + } + return 0; +} + + +// these functions are used for allocating temporary memory +// while decoding. if you can afford the stack space, use +// alloca(); otherwise, provide a temp buffer and it will +// allocate out of those. + +#define array_size_required(count,size) (count*(sizeof(void *)+(size))) + +#define temp_alloc(f,size) (f->alloc.alloc_buffer ? setup_temp_malloc(f,size) : alloca(size)) +#define temp_free(f,p) 0 +#define temp_alloc_save(f) ((f)->temp_offset) +#define temp_alloc_restore(f,p) ((f)->temp_offset = (p)) + +#define temp_block_array(f,count,size) make_block_array(temp_alloc(f,array_size_required(count,size)), count, size) + +// given a sufficiently large block of memory, make an array of pointers to subblocks of it +static void *make_block_array(void *mem, int count, int size) +{ + int i; + void ** p = (void **) mem; + char *q = (char *) (p + count); + for (i=0; i < count; ++i) { + p[i] = q; + q += size; + } + return p; +} + +static void *setup_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + f->setup_memory_required += sz; + if (f->alloc.alloc_buffer) { + void *p = (char *) f->alloc.alloc_buffer + f->setup_offset; + if (f->setup_offset + sz > f->temp_offset) return NULL; + f->setup_offset += sz; + return p; + } + return sz ? malloc(sz) : NULL; +} + +static void setup_free(vorb *f, void *p) +{ + if (f->alloc.alloc_buffer) return; // do nothing; setup mem is a stack + free(p); +} + +static void *setup_temp_malloc(vorb *f, int sz) +{ + sz = (sz+3) & ~3; + if (f->alloc.alloc_buffer) { + if (f->temp_offset - sz < f->setup_offset) return NULL; + f->temp_offset -= sz; + return (char *) f->alloc.alloc_buffer + f->temp_offset; + } + return malloc(sz); +} + +static void setup_temp_free(vorb *f, void *p, int sz) +{ + if (f->alloc.alloc_buffer) { + f->temp_offset += (sz+3)&~3; + return; + } + free(p); +} + +#define CRC32_POLY 0x04c11db7 // from spec + +static uint32 crc_table[256]; +static void crc32_init(void) +{ + int i,j; + uint32 s; + for(i=0; i < 256; i++) { + for (s=(uint32) i << 24, j=0; j < 8; ++j) + s = (s << 1) ^ (s >= (1U<<31) ? CRC32_POLY : 0); + crc_table[i] = s; + } +} + +STB_FORCEINLINE uint32 crc32_update(uint32 crc, uint8 byte) +{ + return (crc << 8) ^ crc_table[byte ^ (crc >> 24)]; +} + + +// used in setup, and for huffman that doesn't go fast path +static unsigned int bit_reverse(unsigned int n) +{ + n = ((n & 0xAAAAAAAA) >> 1) | ((n & 0x55555555) << 1); + n = ((n & 0xCCCCCCCC) >> 2) | ((n & 0x33333333) << 2); + n = ((n & 0xF0F0F0F0) >> 4) | ((n & 0x0F0F0F0F) << 4); + n = ((n & 0xFF00FF00) >> 8) | ((n & 0x00FF00FF) << 8); + return (n >> 16) | (n << 16); +} + +static float square(float x) +{ + return x*x; +} + +// this is a weird definition of log2() for which log2(1) = 1, log2(2) = 2, log2(4) = 3 +// as required by the specification. fast(?) implementation from stb.h +// @OPTIMIZE: called multiple times per-packet with "constants"; move to setup +static int ilog(int32 n) +{ + static signed char log2_4[16] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4 }; + + if (n < 0) return 0; // signed n returns 0 + + // 2 compares if n < 16, 3 compares otherwise (4 if signed or n > 1<<29) + if (n < (1 << 14)) + if (n < (1 << 4)) return 0 + log2_4[n ]; + else if (n < (1 << 9)) return 5 + log2_4[n >> 5]; + else return 10 + log2_4[n >> 10]; + else if (n < (1 << 24)) + if (n < (1 << 19)) return 15 + log2_4[n >> 15]; + else return 20 + log2_4[n >> 20]; + else if (n < (1 << 29)) return 25 + log2_4[n >> 25]; + else return 30 + log2_4[n >> 30]; +} + +#ifndef M_PI + #define M_PI 3.14159265358979323846264f // from CRC +#endif + +// code length assigned to a value with no huffman encoding +#define NO_CODE 255 + +/////////////////////// LEAF SETUP FUNCTIONS ////////////////////////// +// +// these functions are only called at setup, and only a few times +// per file + +static float float32_unpack(uint32 x) +{ + // from the specification + uint32 mantissa = x & 0x1fffff; + uint32 sign = x & 0x80000000; + uint32 exp = (x & 0x7fe00000) >> 21; + double res = sign ? -(double)mantissa : (double)mantissa; + return (float) ldexp((float)res, exp-788); +} + + +// zlib & jpeg huffman tables assume that the output symbols +// can either be arbitrarily arranged, or have monotonically +// increasing frequencies--they rely on the lengths being sorted; +// this makes for a very simple generation algorithm. +// vorbis allows a huffman table with non-sorted lengths. This +// requires a more sophisticated construction, since symbols in +// order do not map to huffman codes "in order". +static void add_entry(Codebook *c, uint32 huff_code, int symbol, int count, int len, uint32 *values) +{ + if (!c->sparse) { + c->codewords [symbol] = huff_code; + } else { + c->codewords [count] = huff_code; + c->codeword_lengths[count] = len; + values [count] = symbol; + } +} + +static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) +{ + int i,k,m=0; + uint32 available[32]; + + memset(available, 0, sizeof(available)); + // find the first entry + for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; + if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + // add to the list + add_entry(c, 0, k, m++, len[k], values); + // add all available leaves + for (i=1; i <= len[k]; ++i) + available[i] = 1U << (32-i); + // note that the above code treats the first case specially, + // but it's really the same as the following code, so they + // could probably be combined (except the initial code is 0, + // and I use 0 in available[] to mean 'empty') + for (i=k+1; i < n; ++i) { + uint32 res; + int z = len[i], y; + if (z == NO_CODE) continue; + // find lowest available leaf (should always be earliest, + // which is what the specification calls for) + // note that this property, and the fact we can never have + // more than one free leaf at a given level, isn't totally + // trivial to prove, but it seems true and the assert never + // fires, so! + while (z > 0 && !available[z]) --z; + if (z == 0) { return FALSE; } + res = available[z]; + assert(z >= 0 && z < 32); + available[z] = 0; + add_entry(c, bit_reverse(res), i, m++, len[i], values); + // propogate availability up the tree + if (z != len[i]) { + assert(len[i] >= 0 && len[i] < 32); + for (y=len[i]; y > z; --y) { + assert(available[y] == 0); + available[y] = res + (1 << (32-y)); + } + } + } + return TRUE; +} + +// accelerated huffman table allows fast O(1) match of all symbols +// of length <= STB_VORBIS_FAST_HUFFMAN_LENGTH +static void compute_accelerated_huffman(Codebook *c) +{ + int i, len; + for (i=0; i < FAST_HUFFMAN_TABLE_SIZE; ++i) + c->fast_huffman[i] = -1; + + len = c->sparse ? c->sorted_entries : c->entries; + #ifdef STB_VORBIS_FAST_HUFFMAN_SHORT + if (len > 32767) len = 32767; // largest possible value we can encode! + #endif + for (i=0; i < len; ++i) { + if (c->codeword_lengths[i] <= STB_VORBIS_FAST_HUFFMAN_LENGTH) { + uint32 z = c->sparse ? bit_reverse(c->sorted_codewords[i]) : c->codewords[i]; + // set table entries for all bit combinations in the higher bits + while (z < FAST_HUFFMAN_TABLE_SIZE) { + c->fast_huffman[z] = i; + z += 1 << c->codeword_lengths[i]; + } + } + } +} + +#ifndef STBV_CDECL +#ifdef _MSC_VER +#define STBV_CDECL __cdecl +#else +#define STBV_CDECL +#endif +#endif + +static int STBV_CDECL uint32_compare(const void *p, const void *q) +{ + uint32 x = * (uint32 *) p; + uint32 y = * (uint32 *) q; + return x < y ? -1 : x > y; +} + +static int include_in_sort(Codebook *c, uint8 len) +{ + if (c->sparse) { assert(len != NO_CODE); return TRUE; } + if (len == NO_CODE) return FALSE; + if (len > STB_VORBIS_FAST_HUFFMAN_LENGTH) return TRUE; + return FALSE; +} + +// if the fast table above doesn't work, we want to binary +// search them... need to reverse the bits +static void compute_sorted_huffman(Codebook *c, uint8 *lengths, uint32 *values) +{ + int i, len; + // build a list of all the entries + // OPTIMIZATION: don't include the short ones, since they'll be caught by FAST_HUFFMAN. + // this is kind of a frivolous optimization--I don't see any performance improvement, + // but it's like 4 extra lines of code, so. + if (!c->sparse) { + int k = 0; + for (i=0; i < c->entries; ++i) + if (include_in_sort(c, lengths[i])) + c->sorted_codewords[k++] = bit_reverse(c->codewords[i]); + assert(k == c->sorted_entries); + } else { + for (i=0; i < c->sorted_entries; ++i) + c->sorted_codewords[i] = bit_reverse(c->codewords[i]); + } + + qsort(c->sorted_codewords, c->sorted_entries, sizeof(c->sorted_codewords[0]), uint32_compare); + c->sorted_codewords[c->sorted_entries] = 0xffffffff; + + len = c->sparse ? c->sorted_entries : c->entries; + // now we need to indicate how they correspond; we could either + // #1: sort a different data structure that says who they correspond to + // #2: for each sorted entry, search the original list to find who corresponds + // #3: for each original entry, find the sorted entry + // #1 requires extra storage, #2 is slow, #3 can use binary search! + for (i=0; i < len; ++i) { + int huff_len = c->sparse ? lengths[values[i]] : lengths[i]; + if (include_in_sort(c,huff_len)) { + uint32 code = bit_reverse(c->codewords[i]); + int x=0, n=c->sorted_entries; + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + assert(c->sorted_codewords[x] == code); + if (c->sparse) { + c->sorted_values[x] = values[i]; + c->codeword_lengths[x] = huff_len; + } else { + c->sorted_values[x] = i; + } + } + } +} + +// only run while parsing the header (3 times) +static int vorbis_validate(uint8 *data) +{ + static uint8 vorbis[6] = { 'v', 'o', 'r', 'b', 'i', 's' }; + return memcmp(data, vorbis, 6) == 0; +} + +// called from setup only, once per code book +// (formula implied by specification) +static int lookup1_values(int entries, int dim) +{ + int r = (int) floor(exp((float) log((float) entries) / dim)); + if ((int) floor(pow((float) r+1, dim)) <= entries) // (int) cast for MinGW warning; + ++r; // floor() to avoid _ftol() when non-CRT + assert(pow((float) r+1, dim) > entries); + assert((int) floor(pow((float) r, dim)) <= entries); // (int),floor() as above + return r; +} + +// called twice per file +static void compute_twiddle_factors(int n, float *A, float *B, float *C) +{ + int n4 = n >> 2, n8 = n >> 3; + int k,k2; + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2) * 0.5f; + B[k2+1] = (float) sin((k2+1)*M_PI/n/2) * 0.5f; + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } +} + +static void compute_window(int n, float *window) +{ + int n2 = n >> 1, i; + for (i=0; i < n2; ++i) + window[i] = (float) sin(0.5 * M_PI * square((float) sin((i - 0 + 0.5) / n2 * 0.5 * M_PI))); +} + +static void compute_bitreverse(int n, uint16 *rev) +{ + int ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + int i, n8 = n >> 3; + for (i=0; i < n8; ++i) + rev[i] = (bit_reverse(i) >> (32-ld+3)) << 2; +} + +static int init_blocksize(vorb *f, int b, int n) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3; + f->A[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->B[b] = (float *) setup_malloc(f, sizeof(float) * n2); + f->C[b] = (float *) setup_malloc(f, sizeof(float) * n4); + if (!f->A[b] || !f->B[b] || !f->C[b]) return error(f, VORBIS_outofmem); + compute_twiddle_factors(n, f->A[b], f->B[b], f->C[b]); + f->window[b] = (float *) setup_malloc(f, sizeof(float) * n2); + if (!f->window[b]) return error(f, VORBIS_outofmem); + compute_window(n, f->window[b]); + f->bit_reverse[b] = (uint16 *) setup_malloc(f, sizeof(uint16) * n8); + if (!f->bit_reverse[b]) return error(f, VORBIS_outofmem); + compute_bitreverse(n, f->bit_reverse[b]); + return TRUE; +} + +static void neighbors(uint16 *x, int n, int *plow, int *phigh) +{ + int low = -1; + int high = 65536; + int i; + for (i=0; i < n; ++i) { + if (x[i] > low && x[i] < x[n]) { *plow = i; low = x[i]; } + if (x[i] < high && x[i] > x[n]) { *phigh = i; high = x[i]; } + } +} + +// this has been repurposed so y is now the original index instead of y +typedef struct +{ + uint16 x,id; +} stbv__floor_ordering; + +static int STBV_CDECL point_compare(const void *p, const void *q) +{ + stbv__floor_ordering *a = (stbv__floor_ordering *) p; + stbv__floor_ordering *b = (stbv__floor_ordering *) q; + return a->x < b->x ? -1 : a->x > b->x; +} + +// +/////////////////////// END LEAF SETUP FUNCTIONS ////////////////////////// + + +#ifdef __SDL_SOUND_INTERNAL__ + #define USE_MEMORY(z) FALSE +#elif defined(STB_VORBIS_NO_STDIO) + #define USE_MEMORY(z) TRUE +#else + #define USE_MEMORY(z) ((z)->stream) +#endif + +static uint8 get8(vorb *z) +{ + if (USE_MEMORY(z)) { + if (z->stream >= z->stream_end) { z->eof = TRUE; return 0; } + return *z->stream++; + } + + #ifdef __SDL_SOUND_INTERNAL__ + { + uint8 c; + if (SDL_RWread(z->rwops, &c, 1, 1) != 1) { z->eof = TRUE; return 0; } + return c; + } + #endif + + #ifndef STB_VORBIS_NO_STDIO + { + int c = fgetc(z->f); + if (c == EOF) { z->eof = TRUE; return 0; } + return c; + } + #endif +} + +static uint32 get32(vorb *f) +{ + uint32 x; + x = get8(f); + x += get8(f) << 8; + x += get8(f) << 16; + x += (uint32) get8(f) << 24; + return x; +} + +static int getn(vorb *z, uint8 *data, int n) +{ + if (USE_MEMORY(z)) { + if (z->stream+n > z->stream_end) { z->eof = 1; return 0; } + memcpy(data, z->stream, n); + z->stream += n; + return 1; + } + + #ifdef __SDL_SOUND_INTERNAL__ + { + if (SDL_RWread(z->rwops, data, n, 1) == 1) { return 1; } + z->eof = 1; + return 0; + } + #endif + + #ifndef STB_VORBIS_NO_STDIO + if (fread(data, n, 1, z->f) == 1) + return 1; + else { + z->eof = 1; + return 0; + } + #endif +} + +static void skip(vorb *z, int n) +{ + if (USE_MEMORY(z)) { + z->stream += n; + if (z->stream >= z->stream_end) z->eof = 1; + return; + } + #ifdef __SDL_SOUND_INTERNAL__ + { + SDL_RWseek(z->rwops, n, RW_SEEK_CUR); + } + #endif + + #ifndef STB_VORBIS_NO_STDIO + { + long x = ftell(z->f); + fseek(z->f, x+n, SEEK_SET); + } + #endif +} + +static int set_file_offset(stb_vorbis *f, unsigned int loc) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + f->eof = 0; + if (USE_MEMORY(f)) { + if (f->stream_start + loc >= f->stream_end || f->stream_start + loc < f->stream_start) { + f->stream = f->stream_end; + f->eof = 1; + return 0; + } else { + f->stream = f->stream_start + loc; + return 1; + } + } + + #ifdef __SDL_SOUND_INTERNAL__ + { + if (loc + f->rwops_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->rwops_start; + } + if (SDL_RWseek(f->rwops, loc, RW_SEEK_SET) != -1) + return 1; + f->eof = 1; + SDL_RWseek(f->rwops, f->rwops_start, RW_SEEK_END); + return 0; + } + #endif + + #ifndef STB_VORBIS_NO_STDIO + if (loc + f->f_start < loc || loc >= 0x80000000) { + loc = 0x7fffffff; + f->eof = 1; + } else { + loc += f->f_start; + } + if (!fseek(f->f, loc, SEEK_SET)) + return 1; + f->eof = 1; + fseek(f->f, f->f_start, SEEK_END); + return 0; + #endif +} + + +static uint8 ogg_page_header[4] = { 0x4f, 0x67, 0x67, 0x53 }; + +static int capture_pattern(vorb *f) +{ + if (0x4f != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x67 != get8(f)) return FALSE; + if (0x53 != get8(f)) return FALSE; + return TRUE; +} + +#define PAGEFLAG_continued_packet 1 +#define PAGEFLAG_first_page 2 +#define PAGEFLAG_last_page 4 + +static int start_page_no_capturepattern(vorb *f) +{ + uint32 loc0,loc1,n; + // stream structure version + if (0 != get8(f)) return error(f, VORBIS_invalid_stream_structure_version); + // header flag + f->page_flag = get8(f); + // absolute granule position + loc0 = get32(f); + loc1 = get32(f); + // @TODO: validate loc0,loc1 as valid positions? + // stream serial number -- vorbis doesn't interleave, so discard + get32(f); + //if (f->serial != get32(f)) return error(f, VORBIS_incorrect_stream_serial_number); + // page sequence number + n = get32(f); + f->last_page = n; + // CRC32 + get32(f); + // page_segments + f->segment_count = get8(f); + if (!getn(f, f->segments, f->segment_count)) + return error(f, VORBIS_unexpected_eof); + // assume we _don't_ know any the sample position of any segments + f->end_seg_with_known_loc = -2; + if (loc0 != ~0U || loc1 != ~0U) { + int i; + // determine which packet is the last one that will complete + for (i=f->segment_count-1; i >= 0; --i) + if (f->segments[i] < 255) + break; + // 'i' is now the index of the _last_ segment of a packet that ends + if (i >= 0) { + f->end_seg_with_known_loc = i; + f->known_loc_for_packet = loc0; + } + } + if (f->first_decode) { + int i,len; + ProbedPage p; + len = 0; + for (i=0; i < f->segment_count; ++i) + len += f->segments[i]; + len += 27 + f->segment_count; + p.page_start = f->first_audio_page_offset; + p.page_end = p.page_start + len; + p.last_decoded_sample = loc0; + f->p_first = p; + } + f->next_seg = 0; + return TRUE; +} + +static int start_page(vorb *f) +{ + if (!capture_pattern(f)) return error(f, VORBIS_missing_capture_pattern); + return start_page_no_capturepattern(f); +} + +static int start_packet(vorb *f) +{ + while (f->next_seg == -1) { + if (!start_page(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) + return error(f, VORBIS_continued_packet_flag_invalid); + } + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + // f->next_seg is now valid + return TRUE; +} + +static int maybe_start_packet(vorb *f) +{ + if (f->next_seg == -1) { + int x = get8(f); + if (f->eof) return FALSE; // EOF at page boundary is not an error! + if (0x4f != x ) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x67 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (0x53 != get8(f)) return error(f, VORBIS_missing_capture_pattern); + if (!start_page_no_capturepattern(f)) return FALSE; + if (f->page_flag & PAGEFLAG_continued_packet) { + // set up enough state that we can read this packet if we want, + // e.g. during recovery + f->last_seg = FALSE; + f->bytes_in_seg = 0; + return error(f, VORBIS_continued_packet_flag_invalid); + } + } + return start_packet(f); +} + +static int next_segment(vorb *f) +{ + int len; + if (f->last_seg) return 0; + if (f->next_seg == -1) { + f->last_seg_which = f->segment_count-1; // in case start_page fails + if (!start_page(f)) { f->last_seg = 1; return 0; } + if (!(f->page_flag & PAGEFLAG_continued_packet)) return error(f, VORBIS_continued_packet_flag_invalid); + } + len = f->segments[f->next_seg++]; + if (len < 255) { + f->last_seg = TRUE; + f->last_seg_which = f->next_seg-1; + } + if (f->next_seg >= f->segment_count) + f->next_seg = -1; + assert(f->bytes_in_seg == 0); + f->bytes_in_seg = len; + return len; +} + +#define EOP (-1) +#define INVALID_BITS (-1) + +static int get8_packet_raw(vorb *f) +{ + if (!f->bytes_in_seg) { // CLANG! + if (f->last_seg) return EOP; + else if (!next_segment(f)) return EOP; + } + assert(f->bytes_in_seg > 0); + --f->bytes_in_seg; + ++f->packet_bytes; + return get8(f); +} + +static int get8_packet(vorb *f) +{ + int x = get8_packet_raw(f); + f->valid_bits = 0; + return x; +} + +static void flush_packet(vorb *f) +{ + while (get8_packet_raw(f) != EOP); +} + +// @OPTIMIZE: this is the secondary bit decoder, so it's probably not as important +// as the huffman decoder? +static uint32 get_bits(vorb *f, int n) +{ + uint32 z; + + if (f->valid_bits < 0) return 0; + if (f->valid_bits < n) { + if (n > 24) { + // the accumulator technique below would not work correctly in this case + z = get_bits(f, 24); + z += get_bits(f, n-24) << 24; + return z; + } + if (f->valid_bits == 0) f->acc = 0; + while (f->valid_bits < n) { + int z = get8_packet_raw(f); + if (z == EOP) { + f->valid_bits = INVALID_BITS; + return 0; + } + f->acc += z << f->valid_bits; + f->valid_bits += 8; + } + } + if (f->valid_bits < 0) return 0; + z = f->acc & ((1 << n)-1); + f->acc >>= n; + f->valid_bits -= n; + return z; +} + +// @OPTIMIZE: primary accumulator for huffman +// expand the buffer to as many bits as possible without reading off end of packet +// it might be nice to allow f->valid_bits and f->acc to be stored in registers, +// e.g. cache them locally and decode locally +STB_FORCEINLINE void prep_huffman(vorb *f) +{ + if (f->valid_bits <= 24) { + if (f->valid_bits == 0) f->acc = 0; + do { + int z; + if (f->last_seg && !f->bytes_in_seg) return; + z = get8_packet_raw(f); + if (z == EOP) return; + f->acc += (unsigned) z << f->valid_bits; + f->valid_bits += 8; + } while (f->valid_bits <= 24); + } +} + +enum +{ + VORBIS_packet_id = 1, + VORBIS_packet_comment = 3, + VORBIS_packet_setup = 5 +}; + +static int codebook_decode_scalar_raw(vorb *f, Codebook *c) +{ + int i; + prep_huffman(f); + + if (c->codewords == NULL && c->sorted_codewords == NULL) + return -1; + + // cases to use binary search: sorted_codewords && !c->codewords + // sorted_codewords && c->entries > 8 + if (c->entries > 8 ? c->sorted_codewords!=NULL : !c->codewords) { + // binary search + uint32 code = bit_reverse(f->acc); + int x=0, n=c->sorted_entries, len; + + while (n > 1) { + // invariant: sc[x] <= code < sc[x+n] + int m = x + (n >> 1); + if (c->sorted_codewords[m] <= code) { + x = m; + n -= (n>>1); + } else { + n >>= 1; + } + } + // x is now the sorted index + if (!c->sparse) x = c->sorted_values[x]; + // x is now sorted index if sparse, or symbol otherwise + len = c->codeword_lengths[x]; + if (f->valid_bits >= len) { + f->acc >>= len; + f->valid_bits -= len; + return x; + } + + f->valid_bits = 0; + return -1; + } + + // if small, linear search + assert(!c->sparse); + for (i=0; i < c->entries; ++i) { + if (c->codeword_lengths[i] == NO_CODE) continue; + if (c->codewords[i] == (f->acc & ((1 << c->codeword_lengths[i])-1))) { + if (f->valid_bits >= c->codeword_lengths[i]) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + return i; + } + f->valid_bits = 0; + return -1; + } + } + + error(f, VORBIS_invalid_stream); + f->valid_bits = 0; + return -1; +} + +#ifndef STB_VORBIS_NO_INLINE_DECODE + +#define DECODE_RAW(var, f,c) \ + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) \ + prep_huffman(f); \ + var = f->acc & FAST_HUFFMAN_TABLE_MASK; \ + var = c->fast_huffman[var]; \ + if (var >= 0) { \ + int n = c->codeword_lengths[var]; \ + f->acc >>= n; \ + f->valid_bits -= n; \ + if (f->valid_bits < 0) { f->valid_bits = 0; var = -1; } \ + } else { \ + var = codebook_decode_scalar_raw(f,c); \ + } + +#else + +static int codebook_decode_scalar(vorb *f, Codebook *c) +{ + int i; + if (f->valid_bits < STB_VORBIS_FAST_HUFFMAN_LENGTH) + prep_huffman(f); + // fast huffman table lookup + i = f->acc & FAST_HUFFMAN_TABLE_MASK; + i = c->fast_huffman[i]; + if (i >= 0) { + f->acc >>= c->codeword_lengths[i]; + f->valid_bits -= c->codeword_lengths[i]; + if (f->valid_bits < 0) { f->valid_bits = 0; return -1; } + return i; + } + return codebook_decode_scalar_raw(f,c); +} + +#define DECODE_RAW(var,f,c) var = codebook_decode_scalar(f,c); + +#endif + +#define DECODE(var,f,c) \ + DECODE_RAW(var,f,c) \ + if (c->sparse) var = c->sorted_values[var]; + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + #define DECODE_VQ(var,f,c) DECODE_RAW(var,f,c) +#else + #define DECODE_VQ(var,f,c) DECODE(var,f,c) +#endif + + + + + + +// CODEBOOK_ELEMENT_FAST is an optimization for the CODEBOOK_FLOATS case +// where we avoid one addition +#define CODEBOOK_ELEMENT(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_FAST(c,off) (c->multiplicands[off]) +#define CODEBOOK_ELEMENT_BASE(c) (0) + +static int codebook_decode_start(vorb *f, Codebook *c) +{ + int z = -1; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) + error(f, VORBIS_invalid_stream); + else { + DECODE_VQ(z,f,c); + if (c->sparse) assert(z < c->sorted_entries); + if (z < 0) { // check for EOP + if (!f->bytes_in_seg) + if (f->last_seg) + return z; + error(f, VORBIS_invalid_stream); + } + } + return z; +} + +static int codebook_decode(vorb *f, Codebook *c, float *output, int len) +{ + int i,z = codebook_decode_start(f,c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + float last = CODEBOOK_ELEMENT_BASE(c); + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i] += val; + if (c->sequence_p) last = val + c->minimum_value; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + if (c->sequence_p) { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i] += val; + last = val + c->minimum_value; + } + } else { + float last = CODEBOOK_ELEMENT_BASE(c); + for (i=0; i < len; ++i) { + output[i] += CODEBOOK_ELEMENT_FAST(c,z+i) + last; + } + } + + return TRUE; +} + +static int codebook_decode_step(vorb *f, Codebook *c, float *output, int len, int step) +{ + int i,z = codebook_decode_start(f,c); + float last = CODEBOOK_ELEMENT_BASE(c); + if (z < 0) return FALSE; + if (len > c->dimensions) len = c->dimensions; + +#ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < len; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + return TRUE; + } +#endif + + z *= c->dimensions; + for (i=0; i < len; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + output[i*step] += val; + if (c->sequence_p) last = val; + } + + return TRUE; +} + +static int codebook_decode_deinterleave_repeat(vorb *f, Codebook *c, float **outputs, int ch, int *c_inter_p, int *p_inter_p, int len, int total_decode) +{ + int c_inter = *c_inter_p; + int p_inter = *p_inter_p; + int i,z, effective = c->dimensions; + + // type 0 is only legal in a scalar context + if (c->lookup_type == 0) return error(f, VORBIS_invalid_stream); + + while (total_decode > 0) { + float last = CODEBOOK_ELEMENT_BASE(c); + DECODE_VQ(z,f,c); + #ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + assert(!c->sparse || z < c->sorted_entries); + #endif + if (z < 0) { + if (!f->bytes_in_seg) + if (f->last_seg) return FALSE; + return error(f, VORBIS_invalid_stream); + } + + // if this will take us off the end of the buffers, stop short! + // we check by computing the length of the virtual interleaved + // buffer (len*ch), our current offset within it (p_inter*ch)+(c_inter), + // and the length we'll be using (effective) + if (c_inter + p_inter*ch + effective > len * ch) { + effective = len*ch - (p_inter*ch - c_inter); + } + + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int div = 1; + for (i=0; i < effective; ++i) { + int off = (z / div) % c->lookup_values; + float val = CODEBOOK_ELEMENT_FAST(c,off) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + if (c->sequence_p) last = val; + div *= c->lookup_values; + } + } else + #endif + { + z *= c->dimensions; + if (c->sequence_p) { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + last = val; + } + } else { + for (i=0; i < effective; ++i) { + float val = CODEBOOK_ELEMENT_FAST(c,z+i) + last; + if (outputs[c_inter]) + outputs[c_inter][p_inter] += val; + if (++c_inter == ch) { c_inter = 0; ++p_inter; } + } + } + } + + total_decode -= effective; + } + *c_inter_p = c_inter; + *p_inter_p = p_inter; + return TRUE; +} + +static int predict_point(int x, int x0, int x1, int y0, int y1) +{ + int dy = y1 - y0; + int adx = x1 - x0; + // @OPTIMIZE: force int division to round in the right direction... is this necessary on x86? + int err = abs(dy) * (x - x0); + int off = err / adx; + return dy < 0 ? y0 - off : y0 + off; +} + +// the following table is block-copied from the specification +static float inverse_db_table[256] = +{ + 1.0649863e-07f, 1.1341951e-07f, 1.2079015e-07f, 1.2863978e-07f, + 1.3699951e-07f, 1.4590251e-07f, 1.5538408e-07f, 1.6548181e-07f, + 1.7623575e-07f, 1.8768855e-07f, 1.9988561e-07f, 2.1287530e-07f, + 2.2670913e-07f, 2.4144197e-07f, 2.5713223e-07f, 2.7384213e-07f, + 2.9163793e-07f, 3.1059021e-07f, 3.3077411e-07f, 3.5226968e-07f, + 3.7516214e-07f, 3.9954229e-07f, 4.2550680e-07f, 4.5315863e-07f, + 4.8260743e-07f, 5.1396998e-07f, 5.4737065e-07f, 5.8294187e-07f, + 6.2082472e-07f, 6.6116941e-07f, 7.0413592e-07f, 7.4989464e-07f, + 7.9862701e-07f, 8.5052630e-07f, 9.0579828e-07f, 9.6466216e-07f, + 1.0273513e-06f, 1.0941144e-06f, 1.1652161e-06f, 1.2409384e-06f, + 1.3215816e-06f, 1.4074654e-06f, 1.4989305e-06f, 1.5963394e-06f, + 1.7000785e-06f, 1.8105592e-06f, 1.9282195e-06f, 2.0535261e-06f, + 2.1869758e-06f, 2.3290978e-06f, 2.4804557e-06f, 2.6416497e-06f, + 2.8133190e-06f, 2.9961443e-06f, 3.1908506e-06f, 3.3982101e-06f, + 3.6190449e-06f, 3.8542308e-06f, 4.1047004e-06f, 4.3714470e-06f, + 4.6555282e-06f, 4.9580707e-06f, 5.2802740e-06f, 5.6234160e-06f, + 5.9888572e-06f, 6.3780469e-06f, 6.7925283e-06f, 7.2339451e-06f, + 7.7040476e-06f, 8.2047000e-06f, 8.7378876e-06f, 9.3057248e-06f, + 9.9104632e-06f, 1.0554501e-05f, 1.1240392e-05f, 1.1970856e-05f, + 1.2748789e-05f, 1.3577278e-05f, 1.4459606e-05f, 1.5399272e-05f, + 1.6400004e-05f, 1.7465768e-05f, 1.8600792e-05f, 1.9809576e-05f, + 2.1096914e-05f, 2.2467911e-05f, 2.3928002e-05f, 2.5482978e-05f, + 2.7139006e-05f, 2.8902651e-05f, 3.0780908e-05f, 3.2781225e-05f, + 3.4911534e-05f, 3.7180282e-05f, 3.9596466e-05f, 4.2169667e-05f, + 4.4910090e-05f, 4.7828601e-05f, 5.0936773e-05f, 5.4246931e-05f, + 5.7772202e-05f, 6.1526565e-05f, 6.5524908e-05f, 6.9783085e-05f, + 7.4317983e-05f, 7.9147585e-05f, 8.4291040e-05f, 8.9768747e-05f, + 9.5602426e-05f, 0.00010181521f, 0.00010843174f, 0.00011547824f, + 0.00012298267f, 0.00013097477f, 0.00013948625f, 0.00014855085f, + 0.00015820453f, 0.00016848555f, 0.00017943469f, 0.00019109536f, + 0.00020351382f, 0.00021673929f, 0.00023082423f, 0.00024582449f, + 0.00026179955f, 0.00027881276f, 0.00029693158f, 0.00031622787f, + 0.00033677814f, 0.00035866388f, 0.00038197188f, 0.00040679456f, + 0.00043323036f, 0.00046138411f, 0.00049136745f, 0.00052329927f, + 0.00055730621f, 0.00059352311f, 0.00063209358f, 0.00067317058f, + 0.00071691700f, 0.00076350630f, 0.00081312324f, 0.00086596457f, + 0.00092223983f, 0.00098217216f, 0.0010459992f, 0.0011139742f, + 0.0011863665f, 0.0012634633f, 0.0013455702f, 0.0014330129f, + 0.0015261382f, 0.0016253153f, 0.0017309374f, 0.0018434235f, + 0.0019632195f, 0.0020908006f, 0.0022266726f, 0.0023713743f, + 0.0025254795f, 0.0026895994f, 0.0028643847f, 0.0030505286f, + 0.0032487691f, 0.0034598925f, 0.0036847358f, 0.0039241906f, + 0.0041792066f, 0.0044507950f, 0.0047400328f, 0.0050480668f, + 0.0053761186f, 0.0057254891f, 0.0060975636f, 0.0064938176f, + 0.0069158225f, 0.0073652516f, 0.0078438871f, 0.0083536271f, + 0.0088964928f, 0.009474637f, 0.010090352f, 0.010746080f, + 0.011444421f, 0.012188144f, 0.012980198f, 0.013823725f, + 0.014722068f, 0.015678791f, 0.016697687f, 0.017782797f, + 0.018938423f, 0.020169149f, 0.021479854f, 0.022875735f, + 0.024362330f, 0.025945531f, 0.027631618f, 0.029427276f, + 0.031339626f, 0.033376252f, 0.035545228f, 0.037855157f, + 0.040315199f, 0.042935108f, 0.045725273f, 0.048696758f, + 0.051861348f, 0.055231591f, 0.058820850f, 0.062643361f, + 0.066714279f, 0.071049749f, 0.075666962f, 0.080584227f, + 0.085821044f, 0.091398179f, 0.097337747f, 0.10366330f, + 0.11039993f, 0.11757434f, 0.12521498f, 0.13335215f, + 0.14201813f, 0.15124727f, 0.16107617f, 0.17154380f, + 0.18269168f, 0.19456402f, 0.20720788f, 0.22067342f, + 0.23501402f, 0.25028656f, 0.26655159f, 0.28387361f, + 0.30232132f, 0.32196786f, 0.34289114f, 0.36517414f, + 0.38890521f, 0.41417847f, 0.44109412f, 0.46975890f, + 0.50028648f, 0.53279791f, 0.56742212f, 0.60429640f, + 0.64356699f, 0.68538959f, 0.72993007f, 0.77736504f, + 0.82788260f, 0.88168307f, 0.9389798f, 1.0f +}; + + +// @OPTIMIZE: if you want to replace this bresenham line-drawing routine, +// note that you must produce bit-identical output to decode correctly; +// this specific sequence of operations is specified in the spec (it's +// drawing integer-quantized frequency-space lines that the encoder +// expects to be exactly the same) +// ... also, isn't the whole point of Bresenham's algorithm to NOT +// have to divide in the setup? sigh. +#ifndef STB_VORBIS_NO_DEFER_FLOOR +#define LINE_OP(a,b) a *= b +#else +#define LINE_OP(a,b) a = b +#endif + +#ifdef STB_VORBIS_DIVIDE_TABLE +#define DIVTAB_NUMER 32 +#define DIVTAB_DENOM 64 +int8 integer_divide_table[DIVTAB_NUMER][DIVTAB_DENOM]; // 2KB +#endif + +STB_FORCEINLINE void draw_line(float *output, int x0, int y0, int x1, int y1, int n) +{ + int dy = y1 - y0; + int adx = x1 - x0; + int ady = abs(dy); + int base; + int x=x0,y=y0; + int err = 0; + int sy; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (adx < DIVTAB_DENOM && ady < DIVTAB_NUMER) { + if (dy < 0) { + base = -integer_divide_table[ady][adx]; + sy = base-1; + } else { + base = integer_divide_table[ady][adx]; + sy = base+1; + } + } else { + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; + } +#else + base = dy / adx; + if (dy < 0) + sy = base - 1; + else + sy = base+1; +#endif + ady -= abs(base) * adx; + if (x1 > n) x1 = n; + if (x < x1) { + LINE_OP(output[x], inverse_db_table[y]); + for (++x; x < x1; ++x) { + err += ady; + if (err >= adx) { + err -= adx; + y += sy; + } else + y += base; + LINE_OP(output[x], inverse_db_table[y]); + } + } +} + +static int residue_decode(vorb *f, Codebook *book, float *target, int offset, int n, int rtype) +{ + int k; + if (rtype == 0) { + int step = n / book->dimensions; + for (k=0; k < step; ++k) + if (!codebook_decode_step(f, book, target+offset+k, n-offset-k, step)) + return FALSE; + } else { + for (k=0; k < n; ) { + if (!codebook_decode(f, book, target+offset, n-k)) + return FALSE; + k += book->dimensions; + offset += book->dimensions; + } + } + return TRUE; +} + +// n is 1/2 of the blocksize -- +// specification: "Correct per-vector decode length is [n]/2" +static void decode_residue(vorb *f, float *residue_buffers[], int ch, int n, int rn, uint8 *do_not_decode) +{ + int i,j,pass; + Residue *r = f->residue_config + rn; + int rtype = f->residue_types[rn]; + int c = r->classbook; + int classwords = f->codebooks[c].dimensions; + unsigned int actual_size = rtype == 2 ? n*2 : n; + unsigned int limit_r_begin = (r->begin < actual_size ? r->begin : actual_size); + unsigned int limit_r_end = (r->end < actual_size ? r->end : actual_size); + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + int temp_alloc_point = temp_alloc_save(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + uint8 ***part_classdata = (uint8 ***) temp_block_array(f,f->channels, part_read * sizeof(**part_classdata)); + #else + int **classifications = (int **) temp_block_array(f,f->channels, part_read * sizeof(**classifications)); + #endif + + CHECK(f); + + for (i=0; i < ch; ++i) + if (!do_not_decode[i]) + memset(residue_buffers[i], 0, sizeof(float) * n); + + if (rtype == 2 && ch != 1) { + for (j=0; j < ch; ++j) + if (!do_not_decode[j]) + break; + if (j == ch) + goto done; + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set = 0; + if (ch == 2) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = (z & 1), p_inter = z>>1; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + #ifdef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #else + // saves 1% + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + #endif + } else { + z += r->part_size; + c_inter = z & 1; + p_inter = z >> 1; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else if (ch == 1) { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = 0, p_inter = z; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = 0; + p_inter = z; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } else { + while (pcount < part_read) { + int z = r->begin + pcount*r->part_size; + int c_inter = z % ch, p_inter = z/ch; + if (pass == 0) { + Codebook *c = f->codebooks+r->classbook; + int q; + DECODE(q,f,c); + if (q == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[0][class_set] = r->classdata[q]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[0][i+pcount] = q % r->classifications; + q /= r->classifications; + } + #endif + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + int z = r->begin + pcount*r->part_size; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[0][class_set][i]; + #else + int c = classifications[0][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + Codebook *book = f->codebooks + b; + if (!codebook_decode_deinterleave_repeat(f, book, residue_buffers, ch, &c_inter, &p_inter, n, r->part_size)) + goto done; + } else { + z += r->part_size; + c_inter = z % ch; + p_inter = z / ch; + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + } + goto done; + } + CHECK(f); + + for (pass=0; pass < 8; ++pass) { + int pcount = 0, class_set=0; + while (pcount < part_read) { + if (pass == 0) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + Codebook *c = f->codebooks+r->classbook; + int temp; + DECODE(temp,f,c); + if (temp == EOP) goto done; + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + part_classdata[j][class_set] = r->classdata[temp]; + #else + for (i=classwords-1; i >= 0; --i) { + classifications[j][i+pcount] = temp % r->classifications; + temp /= r->classifications; + } + #endif + } + } + } + for (i=0; i < classwords && pcount < part_read; ++i, ++pcount) { + for (j=0; j < ch; ++j) { + if (!do_not_decode[j]) { + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + int c = part_classdata[j][class_set][i]; + #else + int c = classifications[j][pcount]; + #endif + int b = r->residue_books[c][pass]; + if (b >= 0) { + float *target = residue_buffers[j]; + int offset = r->begin + pcount * r->part_size; + int n = r->part_size; + Codebook *book = f->codebooks + b; + if (!residue_decode(f, book, target, offset, n, rtype)) + goto done; + } + } + } + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + ++class_set; + #endif + } + } + done: + CHECK(f); + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + temp_free(f,part_classdata); + #else + temp_free(f,classifications); + #endif + temp_alloc_restore(f,temp_alloc_point); +} + + +#if 0 +// slow way for debugging +void inverse_mdct_slow(float *buffer, int n) +{ + int i,j; + int n2 = n >> 1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + // formula from paper: + //acc += n/4.0f * x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + // formula from wikipedia + //acc += 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + // these are equivalent, except the formula from the paper inverts the multiplier! + // however, what actually works is NO MULTIPLIER!?! + //acc += 64 * 2.0f / n2 * x[j] * (float) cos(M_PI/n2 * (i + 0.5 + n2/2)*(j + 0.5)); + acc += x[j] * (float) cos(M_PI / 2 / n * (2 * i + 1 + n/2.0)*(2*j+1)); + buffer[i] = acc; + } + free(x); +} +#elif 0 +// same as above, but just barely able to run in real time on modern machines +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + float mcos[16384]; + int i,j; + int n2 = n >> 1, nmask = (n << 2) -1; + float *x = (float *) malloc(sizeof(*x) * n2); + memcpy(x, buffer, sizeof(*x) * n2); + for (i=0; i < 4*n; ++i) + mcos[i] = (float) cos(M_PI / 2 * i / n); + + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n2; ++j) + acc += x[j] * mcos[(2 * i + 1 + n2)*(2*j+1) & nmask]; + buffer[i] = acc; + } + free(x); +} +#elif 0 +// transform to use a slow dct-iv; this is STILL basically trivial, +// but only requires half as many ops +void dct_iv_slow(float *buffer, int n) +{ + float mcos[16384]; + float x[2048]; + int i,j; + int n2 = n >> 1, nmask = (n << 3) - 1; + memcpy(x, buffer, sizeof(*x) * n); + for (i=0; i < 8*n; ++i) + mcos[i] = (float) cos(M_PI / 4 * i / n); + for (i=0; i < n; ++i) { + float acc = 0; + for (j=0; j < n; ++j) + acc += x[j] * mcos[((2 * i + 1)*(2*j+1)) & nmask]; + buffer[i] = acc; + } +} + +void inverse_mdct_slow(float *buffer, int n, vorb *f, int blocktype) +{ + int i, n4 = n >> 2, n2 = n >> 1, n3_4 = n - n4; + float temp[4096]; + + memcpy(temp, buffer, n2 * sizeof(float)); + dct_iv_slow(temp, n2); // returns -c'-d, a-b' + + for (i=0; i < n4 ; ++i) buffer[i] = temp[i+n4]; // a-b' + for ( ; i < n3_4; ++i) buffer[i] = -temp[n3_4 - i - 1]; // b-a', c+d' + for ( ; i < n ; ++i) buffer[i] = -temp[i - n3_4]; // c'+d +} +#endif + +#ifndef LIBVORBIS_MDCT +#define LIBVORBIS_MDCT 0 +#endif + +#if LIBVORBIS_MDCT +// directly call the vorbis MDCT using an interface documented +// by Jeff Roberts... useful for performance comparison +typedef struct +{ + int n; + int log2n; + + float *trig; + int *bitrev; + + float scale; +} mdct_lookup; + +extern void mdct_init(mdct_lookup *lookup, int n); +extern void mdct_clear(mdct_lookup *l); +extern void mdct_backward(mdct_lookup *init, float *in, float *out); + +mdct_lookup M1,M2; + +void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + mdct_lookup *M; + if (M1.n == n) M = &M1; + else if (M2.n == n) M = &M2; + else if (M1.n == 0) { mdct_init(&M1, n); M = &M1; } + else { + if (M2.n) __asm int 3; + mdct_init(&M2, n); + M = &M2; + } + + mdct_backward(M, buffer, buffer); +} +#endif + + +// the following were split out into separate functions while optimizing; +// they could be pushed back up but eh. STB_FORCEINLINE showed no change; +// they're probably already being inlined. +static void imdct_step3_iter0_loop(int n, float *e, int i_off, int k_off, float *A) +{ + float *ee0 = e + i_off; + float *ee2 = ee0 + k_off; + int i; + + assert((n & 3) == 0); + for (i=(n>>2); i > 0; --i) { + float k00_20, k01_21; + k00_20 = ee0[ 0] - ee2[ 0]; + k01_21 = ee0[-1] - ee2[-1]; + ee0[ 0] += ee2[ 0];//ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] += ee2[-1];//ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-1] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-2] - ee2[-2]; + k01_21 = ee0[-3] - ee2[-3]; + ee0[-2] += ee2[-2];//ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] += ee2[-3];//ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-3] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-4] - ee2[-4]; + k01_21 = ee0[-5] - ee2[-5]; + ee0[-4] += ee2[-4];//ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] += ee2[-5];//ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-5] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + + k00_20 = ee0[-6] - ee2[-6]; + k01_21 = ee0[-7] - ee2[-7]; + ee0[-6] += ee2[-6];//ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] += ee2[-7];//ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = k00_20 * A[0] - k01_21 * A[1]; + ee2[-7] = k01_21 * A[0] + k00_20 * A[1]; + A += 8; + ee0 -= 8; + ee2 -= 8; + } +} + +static void imdct_step3_inner_r_loop(int lim, float *e, int d0, int k_off, float *A, int k1) +{ + int i; + float k00_20, k01_21; + + float *e0 = e + d0; + float *e2 = e0 + k_off; + + for (i=lim >> 2; i > 0; --i) { + k00_20 = e0[-0] - e2[-0]; + k01_21 = e0[-1] - e2[-1]; + e0[-0] += e2[-0];//e0[-0] = e0[-0] + e2[-0]; + e0[-1] += e2[-1];//e0[-1] = e0[-1] + e2[-1]; + e2[-0] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-1] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-2] - e2[-2]; + k01_21 = e0[-3] - e2[-3]; + e0[-2] += e2[-2];//e0[-2] = e0[-2] + e2[-2]; + e0[-3] += e2[-3];//e0[-3] = e0[-3] + e2[-3]; + e2[-2] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-3] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-4] - e2[-4]; + k01_21 = e0[-5] - e2[-5]; + e0[-4] += e2[-4];//e0[-4] = e0[-4] + e2[-4]; + e0[-5] += e2[-5];//e0[-5] = e0[-5] + e2[-5]; + e2[-4] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-5] = (k01_21)*A[0] + (k00_20) * A[1]; + + A += k1; + + k00_20 = e0[-6] - e2[-6]; + k01_21 = e0[-7] - e2[-7]; + e0[-6] += e2[-6];//e0[-6] = e0[-6] + e2[-6]; + e0[-7] += e2[-7];//e0[-7] = e0[-7] + e2[-7]; + e2[-6] = (k00_20)*A[0] - (k01_21) * A[1]; + e2[-7] = (k01_21)*A[0] + (k00_20) * A[1]; + + e0 -= 8; + e2 -= 8; + + A += k1; + } +} + +static void imdct_step3_inner_s_loop(int n, float *e, int i_off, int k_off, float *A, int a_off, int k0) +{ + int i; + float A0 = A[0]; + float A1 = A[0+1]; + float A2 = A[0+a_off]; + float A3 = A[0+a_off+1]; + float A4 = A[0+a_off*2+0]; + float A5 = A[0+a_off*2+1]; + float A6 = A[0+a_off*3+0]; + float A7 = A[0+a_off*3+1]; + + float k00,k11; + + float *ee0 = e +i_off; + float *ee2 = ee0+k_off; + + for (i=n; i > 0; --i) { + k00 = ee0[ 0] - ee2[ 0]; + k11 = ee0[-1] - ee2[-1]; + ee0[ 0] = ee0[ 0] + ee2[ 0]; + ee0[-1] = ee0[-1] + ee2[-1]; + ee2[ 0] = (k00) * A0 - (k11) * A1; + ee2[-1] = (k11) * A0 + (k00) * A1; + + k00 = ee0[-2] - ee2[-2]; + k11 = ee0[-3] - ee2[-3]; + ee0[-2] = ee0[-2] + ee2[-2]; + ee0[-3] = ee0[-3] + ee2[-3]; + ee2[-2] = (k00) * A2 - (k11) * A3; + ee2[-3] = (k11) * A2 + (k00) * A3; + + k00 = ee0[-4] - ee2[-4]; + k11 = ee0[-5] - ee2[-5]; + ee0[-4] = ee0[-4] + ee2[-4]; + ee0[-5] = ee0[-5] + ee2[-5]; + ee2[-4] = (k00) * A4 - (k11) * A5; + ee2[-5] = (k11) * A4 + (k00) * A5; + + k00 = ee0[-6] - ee2[-6]; + k11 = ee0[-7] - ee2[-7]; + ee0[-6] = ee0[-6] + ee2[-6]; + ee0[-7] = ee0[-7] + ee2[-7]; + ee2[-6] = (k00) * A6 - (k11) * A7; + ee2[-7] = (k11) * A6 + (k00) * A7; + + ee0 -= k0; + ee2 -= k0; + } +} + +STB_FORCEINLINE void iter_54(float *z) +{ + float k00,k11,k22,k33; + float y0,y1,y2,y3; + + k00 = z[ 0] - z[-4]; + y0 = z[ 0] + z[-4]; + y2 = z[-2] + z[-6]; + k22 = z[-2] - z[-6]; + + z[-0] = y0 + y2; // z0 + z4 + z2 + z6 + z[-2] = y0 - y2; // z0 + z4 - z2 - z6 + + // done with y0,y2 + + k33 = z[-3] - z[-7]; + + z[-4] = k00 + k33; // z0 - z4 + z3 - z7 + z[-6] = k00 - k33; // z0 - z4 - z3 + z7 + + // done with k33 + + k11 = z[-1] - z[-5]; + y1 = z[-1] + z[-5]; + y3 = z[-3] + z[-7]; + + z[-1] = y1 + y3; // z1 + z5 + z3 + z7 + z[-3] = y1 - y3; // z1 + z5 - z3 - z7 + z[-5] = k11 - k22; // z1 - z5 + z2 - z6 + z[-7] = k11 + k22; // z1 - z5 - z2 + z6 +} + +static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, int base_n) +{ + int a_off = base_n >> 3; + float A2 = A[0+a_off]; + float *z = e + i_off; + float *base = z - 16 * n; + + while (z > base) { + float k00,k11; + + k00 = z[-0] - z[-8]; + k11 = z[-1] - z[-9]; + z[-0] = z[-0] + z[-8]; + z[-1] = z[-1] + z[-9]; + z[-8] = k00; + z[-9] = k11 ; + + k00 = z[ -2] - z[-10]; + k11 = z[ -3] - z[-11]; + z[ -2] = z[ -2] + z[-10]; + z[ -3] = z[ -3] + z[-11]; + z[-10] = (k00+k11) * A2; + z[-11] = (k11-k00) * A2; + + k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + k11 = z[ -5] - z[-13]; + z[ -4] = z[ -4] + z[-12]; + z[ -5] = z[ -5] + z[-13]; + z[-12] = k11; + z[-13] = k00; + + k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation + k11 = z[ -7] - z[-15]; + z[ -6] = z[ -6] + z[-14]; + z[ -7] = z[ -7] + z[-15]; + z[-14] = (k00+k11) * A2; + z[-15] = (k00-k11) * A2; + + iter_54(z); + iter_54(z-8); + z -= 16; + } +} + +static void inverse_mdct(float *buffer, int n, vorb *f, int blocktype) +{ + int n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int ld; + // @OPTIMIZE: reduce register pressure by using fewer variables? + int save_point = temp_alloc_save(f); + float *buf2 = (float *) temp_alloc(f, n2 * sizeof(*buf2)); + float *u=NULL,*v=NULL; + // twiddle factors + float *A = f->A[blocktype]; + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // See notes about bugs in that paper in less-optimal implementation 'inverse_mdct_old' after this function. + + // kernel from paper + + + // merged: + // copy and reflect spectral data + // step 0 + + // note that it turns out that the items added together during + // this step are, in fact, being added to themselves (as reflected + // by step 0). inexplicable inefficiency! this became obvious + // once I combined the passes. + + // so there's a missing 'times 2' here (for adding X to itself). + // this propogates through linearly to the end, where the numbers + // are 1/2 too small, and need to be compensated for. + + { + float *d,*e, *AA, *e_stop; + d = &buf2[n2-2]; + AA = A; + e = &buffer[0]; + e_stop = &buffer[n2]; + while (e != e_stop) { + d[1] = (e[0] * AA[0] - e[2]*AA[1]); + d[0] = (e[0] * AA[1] + e[2]*AA[0]); + d -= 2; + AA += 2; + e += 4; + } + + e = &buffer[n2-3]; + while (d >= buf2) { + d[1] = (-e[2] * AA[0] - -e[0]*AA[1]); + d[0] = (-e[2] * AA[1] + -e[0]*AA[0]); + d -= 2; + AA += 2; + e -= 4; + } + } + + // now we use symbolic names for these, so that we can + // possibly swap their meaning as we change which operations + // are in place + + u = buffer; + v = buf2; + + // step 2 (paper output is w, now u) + // this could be in place, but the data ends up in the wrong + // place... _somebody_'s got to swap it, so this is nominated + { + float *AA = &A[n2-8]; + float *d0,*d1, *e0, *e1; + + e0 = &v[n4]; + e1 = &v[0]; + + d0 = &u[n4]; + d1 = &u[0]; + + while (AA >= A) { + float v40_20, v41_21; + + v41_21 = e0[1] - e1[1]; + v40_20 = e0[0] - e1[0]; + d0[1] = e0[1] + e1[1]; + d0[0] = e0[0] + e1[0]; + d1[1] = v41_21*AA[4] - v40_20*AA[5]; + d1[0] = v40_20*AA[4] + v41_21*AA[5]; + + v41_21 = e0[3] - e1[3]; + v40_20 = e0[2] - e1[2]; + d0[3] = e0[3] + e1[3]; + d0[2] = e0[2] + e1[2]; + d1[3] = v41_21*AA[0] - v40_20*AA[1]; + d1[2] = v40_20*AA[0] + v41_21*AA[1]; + + AA -= 8; + + d0 += 4; + d1 += 4; + e0 += 4; + e1 += 4; + } + } + + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + + // optimized step 3: + + // the original step3 loop can be nested r inside s or s inside r; + // it's written originally as s inside r, but this is dumb when r + // iterates many times, and s few. So I have two copies of it and + // switch between them halfway. + + // this is iteration 0 of step 3 + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*0, -(n >> 3), A); + imdct_step3_iter0_loop(n >> 4, u, n2-1-n4*1, -(n >> 3), A); + + // this is iteration 1 of step 3 + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*0, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*1, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*2, -(n >> 4), A, 16); + imdct_step3_inner_r_loop(n >> 5, u, n2-1 - n8*3, -(n >> 4), A, 16); + + l=2; + for (; l < (ld-3)>>1; ++l) { + int k0 = n >> (l+2), k0_2 = k0>>1; + int lim = 1 << (l+1); + int i; + for (i=0; i < lim; ++i) + imdct_step3_inner_r_loop(n >> (l+4), u, n2-1 - k0*i, -k0_2, A, 1 << (l+3)); + } + + for (; l < ld-6; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3), k0_2 = k0>>1; + int rlim = n >> (l+6), r; + int lim = 1 << (l+1); + int i_off; + float *A0 = A; + i_off = n2-1; + for (r=rlim; r > 0; --r) { + imdct_step3_inner_s_loop(lim, u, i_off, -k0_2, A0, k1, k0); + A0 += k1*4; + i_off -= 8; + } + } + + // iterations with count: + // ld-6,-5,-4 all interleaved together + // the big win comes from getting rid of needless flops + // due to the constants on pass 5 & 4 being all 1 and 0; + // combining them to be simultaneous to improve cache made little difference + imdct_step3_inner_s_loop_ld654(n >> 5, u, n2-1, A, n); + + // output is u + + // step 4, 5, and 6 + // cannot be in-place because of step 5 + { + uint16 *bitrev = f->bit_reverse[blocktype]; + // weirdly, I'd have thought reading sequentially and writing + // erratically would have been better than vice-versa, but in + // fact that's not what my testing showed. (That is, with + // j = bitreverse(i), do you read i and write j, or read j and write i.) + + float *d0 = &v[n4-4]; + float *d1 = &v[n2-4]; + while (d0 >= v) { + int k4; + + k4 = bitrev[0]; + d1[3] = u[k4+0]; + d1[2] = u[k4+1]; + d0[3] = u[k4+2]; + d0[2] = u[k4+3]; + + k4 = bitrev[1]; + d1[1] = u[k4+0]; + d1[0] = u[k4+1]; + d0[1] = u[k4+2]; + d0[0] = u[k4+3]; + + d0 -= 4; + d1 -= 4; + bitrev += 2; + } + } + // (paper output is u, now v) + + + // data must be in buf2 + assert(v == buf2); + + // step 7 (paper output is v, now v) + // this is now in place + { + float *C = f->C[blocktype]; + float *d, *e; + + d = v; + e = v + n2 - 4; + + while (d < e) { + float a02,a11,b0,b1,b2,b3; + + a02 = d[0] - e[2]; + a11 = d[1] + e[3]; + + b0 = C[1]*a02 + C[0]*a11; + b1 = C[1]*a11 - C[0]*a02; + + b2 = d[0] + e[ 2]; + b3 = d[1] - e[ 3]; + + d[0] = b2 + b0; + d[1] = b3 + b1; + e[2] = b2 - b0; + e[3] = b1 - b3; + + a02 = d[2] - e[0]; + a11 = d[3] + e[1]; + + b0 = C[3]*a02 + C[2]*a11; + b1 = C[3]*a11 - C[2]*a02; + + b2 = d[2] + e[ 0]; + b3 = d[3] - e[ 1]; + + d[2] = b2 + b0; + d[3] = b3 + b1; + e[0] = b2 - b0; + e[1] = b1 - b3; + + C += 4; + d += 4; + e -= 4; + } + } + + // data must be in buf2 + + + // step 8+decode (paper output is X, now buffer) + // this generates pairs of data a la 8 and pushes them directly through + // the decode kernel (pushing rather than pulling) to avoid having + // to make another pass later + + // this cannot POSSIBLY be in place, so we refer to the buffers directly + + { + float *d0,*d1,*d2,*d3; + + float *B = f->B[blocktype] + n2 - 8; + float *e = buf2 + n2 - 8; + d0 = &buffer[0]; + d1 = &buffer[n2-4]; + d2 = &buffer[n2]; + d3 = &buffer[n-4]; + while (e >= v) { + float p0,p1,p2,p3; + + p3 = e[6]*B[7] - e[7]*B[6]; + p2 = -e[6]*B[6] - e[7]*B[7]; + + d0[0] = p3; + d1[3] = - p3; + d2[0] = p2; + d3[3] = p2; + + p1 = e[4]*B[5] - e[5]*B[4]; + p0 = -e[4]*B[4] - e[5]*B[5]; + + d0[1] = p1; + d1[2] = - p1; + d2[1] = p0; + d3[2] = p0; + + p3 = e[2]*B[3] - e[3]*B[2]; + p2 = -e[2]*B[2] - e[3]*B[3]; + + d0[2] = p3; + d1[1] = - p3; + d2[2] = p2; + d3[1] = p2; + + p1 = e[0]*B[1] - e[1]*B[0]; + p0 = -e[0]*B[0] - e[1]*B[1]; + + d0[3] = p1; + d1[0] = - p1; + d2[3] = p0; + d3[0] = p0; + + B -= 8; + e -= 8; + d0 += 4; + d2 += 4; + d1 -= 4; + d3 -= 4; + } + } + + temp_free(f,buf2); + temp_alloc_restore(f,save_point); +} + +#if 0 +// this is the original version of the above code, if you want to optimize it from scratch +void inverse_mdct_naive(float *buffer, int n) +{ + float s; + float A[1 << 12], B[1 << 12], C[1 << 11]; + int i,k,k2,k4, n2 = n >> 1, n4 = n >> 2, n8 = n >> 3, l; + int n3_4 = n - n4, ld; + // how can they claim this only uses N words?! + // oh, because they're only used sparsely, whoops + float u[1 << 13], X[1 << 13], v[1 << 13], w[1 << 13]; + // set up twiddle factors + + for (k=k2=0; k < n4; ++k,k2+=2) { + A[k2 ] = (float) cos(4*k*M_PI/n); + A[k2+1] = (float) -sin(4*k*M_PI/n); + B[k2 ] = (float) cos((k2+1)*M_PI/n/2); + B[k2+1] = (float) sin((k2+1)*M_PI/n/2); + } + for (k=k2=0; k < n8; ++k,k2+=2) { + C[k2 ] = (float) cos(2*(k2+1)*M_PI/n); + C[k2+1] = (float) -sin(2*(k2+1)*M_PI/n); + } + + // IMDCT algorithm from "The use of multirate filter banks for coding of high quality digital audio" + // Note there are bugs in that pseudocode, presumably due to them attempting + // to rename the arrays nicely rather than representing the way their actual + // implementation bounces buffers back and forth. As a result, even in the + // "some formulars corrected" version, a direct implementation fails. These + // are noted below as "paper bug". + + // copy and reflect spectral data + for (k=0; k < n2; ++k) u[k] = buffer[k]; + for ( ; k < n ; ++k) u[k] = -buffer[n - k - 1]; + // kernel from paper + // step 1 + for (k=k2=k4=0; k < n4; k+=1, k2+=2, k4+=4) { + v[n-k4-1] = (u[k4] - u[n-k4-1]) * A[k2] - (u[k4+2] - u[n-k4-3])*A[k2+1]; + v[n-k4-3] = (u[k4] - u[n-k4-1]) * A[k2+1] + (u[k4+2] - u[n-k4-3])*A[k2]; + } + // step 2 + for (k=k4=0; k < n8; k+=1, k4+=4) { + w[n2+3+k4] = v[n2+3+k4] + v[k4+3]; + w[n2+1+k4] = v[n2+1+k4] + v[k4+1]; + w[k4+3] = (v[n2+3+k4] - v[k4+3])*A[n2-4-k4] - (v[n2+1+k4]-v[k4+1])*A[n2-3-k4]; + w[k4+1] = (v[n2+1+k4] - v[k4+1])*A[n2-4-k4] + (v[n2+3+k4]-v[k4+3])*A[n2-3-k4]; + } + // step 3 + ld = ilog(n) - 1; // ilog is off-by-one from normal definitions + for (l=0; l < ld-3; ++l) { + int k0 = n >> (l+2), k1 = 1 << (l+3); + int rlim = n >> (l+4), r4, r; + int s2lim = 1 << (l+2), s2; + for (r=r4=0; r < rlim; r4+=4,++r) { + for (s2=0; s2 < s2lim; s2+=2) { + u[n-1-k0*s2-r4] = w[n-1-k0*s2-r4] + w[n-1-k0*(s2+1)-r4]; + u[n-3-k0*s2-r4] = w[n-3-k0*s2-r4] + w[n-3-k0*(s2+1)-r4]; + u[n-1-k0*(s2+1)-r4] = (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1] + - (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1+1]; + u[n-3-k0*(s2+1)-r4] = (w[n-3-k0*s2-r4] - w[n-3-k0*(s2+1)-r4]) * A[r*k1] + + (w[n-1-k0*s2-r4] - w[n-1-k0*(s2+1)-r4]) * A[r*k1+1]; + } + } + if (l+1 < ld-3) { + // paper bug: ping-ponging of u&w here is omitted + memcpy(w, u, sizeof(u)); + } + } + + // step 4 + for (i=0; i < n8; ++i) { + int j = bit_reverse(i) >> (32-ld+3); + assert(j < n8); + if (i == j) { + // paper bug: original code probably swapped in place; if copying, + // need to directly copy in this case + int i8 = i << 3; + v[i8+1] = u[i8+1]; + v[i8+3] = u[i8+3]; + v[i8+5] = u[i8+5]; + v[i8+7] = u[i8+7]; + } else if (i < j) { + int i8 = i << 3, j8 = j << 3; + v[j8+1] = u[i8+1], v[i8+1] = u[j8 + 1]; + v[j8+3] = u[i8+3], v[i8+3] = u[j8 + 3]; + v[j8+5] = u[i8+5], v[i8+5] = u[j8 + 5]; + v[j8+7] = u[i8+7], v[i8+7] = u[j8 + 7]; + } + } + // step 5 + for (k=0; k < n2; ++k) { + w[k] = v[k*2+1]; + } + // step 6 + for (k=k2=k4=0; k < n8; ++k, k2 += 2, k4 += 4) { + u[n-1-k2] = w[k4]; + u[n-2-k2] = w[k4+1]; + u[n3_4 - 1 - k2] = w[k4+2]; + u[n3_4 - 2 - k2] = w[k4+3]; + } + // step 7 + for (k=k2=0; k < n8; ++k, k2 += 2) { + v[n2 + k2 ] = ( u[n2 + k2] + u[n-2-k2] + C[k2+1]*(u[n2+k2]-u[n-2-k2]) + C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n-2 - k2] = ( u[n2 + k2] + u[n-2-k2] - C[k2+1]*(u[n2+k2]-u[n-2-k2]) - C[k2]*(u[n2+k2+1]+u[n-2-k2+1]))/2; + v[n2+1+ k2] = ( u[n2+1+k2] - u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + v[n-1 - k2] = (-u[n2+1+k2] + u[n-1-k2] + C[k2+1]*(u[n2+1+k2]+u[n-1-k2]) - C[k2]*(u[n2+k2]-u[n-2-k2]))/2; + } + // step 8 + for (k=k2=0; k < n4; ++k,k2 += 2) { + X[k] = v[k2+n2]*B[k2 ] + v[k2+1+n2]*B[k2+1]; + X[n2-1-k] = v[k2+n2]*B[k2+1] - v[k2+1+n2]*B[k2 ]; + } + + // decode kernel to output + // determined the following value experimentally + // (by first figuring out what made inverse_mdct_slow work); then matching that here + // (probably vorbis encoder premultiplies by n or n/2, to save it on the decoder?) + s = 0.5; // theoretically would be n4 + + // [[[ note! the s value of 0.5 is compensated for by the B[] in the current code, + // so it needs to use the "old" B values to behave correctly, or else + // set s to 1.0 ]]] + for (i=0; i < n4 ; ++i) buffer[i] = s * X[i+n4]; + for ( ; i < n3_4; ++i) buffer[i] = -s * X[n3_4 - i - 1]; + for ( ; i < n ; ++i) buffer[i] = -s * X[i - n3_4]; +} +#endif + +static float *get_window(vorb *f, int len) +{ + len <<= 1; + if (len == f->blocksize_0) return f->window[0]; + if (len == f->blocksize_1) return f->window[1]; + assert(0); + return NULL; +} + +#ifndef STB_VORBIS_NO_DEFER_FLOOR +typedef int16 YTYPE; +#else +typedef int YTYPE; +#endif +static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *finalY, uint8 *step2_flag) +{ + int n2 = n >> 1; + int s = map->chan[i].mux, floor; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + int j,q; + int lx = 0, ly = finalY[0] * g->floor1_multiplier; + for (q=1; q < g->values; ++q) { + j = g->sorted_order[q]; + #ifndef STB_VORBIS_NO_DEFER_FLOOR + if (finalY[j] >= 0) + #else + if (step2_flag[j]) + #endif + { + int hy = finalY[j] * g->floor1_multiplier; + int hx = g->Xlist[j]; + if (lx != hx) + draw_line(target, lx,ly, hx,hy, n2); + CHECK(f); + lx = hx, ly = hy; + } + } + if (lx < n2) { + // optimization of: draw_line(target, lx,ly, n,ly, n2); + for (j=lx; j < n2; ++j) + LINE_OP(target[j], inverse_db_table[ly]); + CHECK(f); + } + } + return TRUE; +} + +// The meaning of "left" and "right" +// +// For a given frame: +// we compute samples from 0..n +// window_center is n/2 +// we'll window and mix the samples from left_start to left_end with data from the previous frame +// all of the samples from left_end to right_start can be output without mixing; however, +// this interval is 0-length except when transitioning between short and long frames +// all of the samples from right_start to right_end need to be mixed with the next frame, +// which we don't have, so those get saved in a buffer +// frame N's right_end-right_start, the number of samples to mix with the next frame, +// has to be the same as frame N+1's left_end-left_start (which they are by +// construction) + +static int vorbis_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + Mode *m; + int i, n, prev, next, window_center; + f->channel_buffer_start = f->channel_buffer_end = 0; + + retry: + if (f->eof) return FALSE; + if (!maybe_start_packet(f)) + return FALSE; + // check packet type + if (get_bits(f,1) != 0) { + if (IS_PUSH_MODE(f)) + return error(f,VORBIS_bad_packet_type); + while (EOP != get8_packet(f)); + goto retry; + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + i = get_bits(f, ilog(f->mode_count-1)); + if (i == EOP) return FALSE; + if (i >= f->mode_count) return FALSE; + *mode = i; + m = f->mode_config + i; + if (m->blockflag) { + n = f->blocksize_1; + prev = get_bits(f,1); + next = get_bits(f,1); + } else { + prev = next = 0; + n = f->blocksize_0; + } + +// WINDOWING + + window_center = n >> 1; + if (m->blockflag && !prev) { + *p_left_start = (n - f->blocksize_0) >> 2; + *p_left_end = (n + f->blocksize_0) >> 2; + } else { + *p_left_start = 0; + *p_left_end = window_center; + } + if (m->blockflag && !next) { + *p_right_start = (n*3 - f->blocksize_0) >> 2; + *p_right_end = (n*3 + f->blocksize_0) >> 2; + } else { + *p_right_start = window_center; + *p_right_end = n; + } + + return TRUE; +} + +static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, int left_end, int right_start, int right_end, int *p_left) +{ + Mapping *map; + int i,j,k,n,n2; + int zero_channel[256]; + int really_zero_channel[256]; + +// WINDOWING + + n = f->blocksize[m->blockflag]; + map = &f->mapping[m->mapping]; + +// FLOORS + n2 = n >> 1; + + CHECK(f); + + for (i=0; i < f->channels; ++i) { + int s = map->chan[i].mux, floor; + zero_channel[i] = FALSE; + floor = map->submap_floor[s]; + if (f->floor_types[floor] == 0) { + return error(f, VORBIS_invalid_stream); + } else { + Floor1 *g = &f->floor_config[floor].floor1; + if (get_bits(f, 1)) { + short *finalY; + uint8 step2_flag[256]; + static int range_list[4] = { 256, 128, 86, 64 }; + int range = range_list[g->floor1_multiplier-1]; + int offset = 2; + finalY = f->finalY[i]; + finalY[0] = get_bits(f, ilog(range)-1); + finalY[1] = get_bits(f, ilog(range)-1); + for (j=0; j < g->partitions; ++j) { + int pclass = g->partition_class_list[j]; + int cdim = g->class_dimensions[pclass]; + int cbits = g->class_subclasses[pclass]; + int csub = (1 << cbits)-1; + int cval = 0; + if (cbits) { + Codebook *c = f->codebooks + g->class_masterbooks[pclass]; + DECODE(cval,f,c); + } + for (k=0; k < cdim; ++k) { + int book = g->subclass_books[pclass][cval & csub]; + cval = cval >> cbits; + if (book >= 0) { + int temp; + Codebook *c = f->codebooks + book; + DECODE(temp,f,c); + finalY[offset++] = temp; + } else + finalY[offset++] = 0; + } + } + if (f->valid_bits == INVALID_BITS) goto error; // behavior according to spec + step2_flag[0] = step2_flag[1] = 1; + for (j=2; j < g->values; ++j) { + int low, high, pred, highroom, lowroom, room, val; + low = g->neighbors[j][0]; + high = g->neighbors[j][1]; + //neighbors(g->Xlist, j, &low, &high); + pred = predict_point(g->Xlist[j], g->Xlist[low], g->Xlist[high], finalY[low], finalY[high]); + val = finalY[j]; + highroom = range - pred; + lowroom = pred; + if (highroom < lowroom) + room = highroom * 2; + else + room = lowroom * 2; + if (val) { + step2_flag[low] = step2_flag[high] = 1; + step2_flag[j] = 1; + if (val >= room) + if (highroom > lowroom) + finalY[j] = val - lowroom + pred; + else + finalY[j] = pred - val + highroom - 1; + else + if (val & 1) + finalY[j] = pred - ((val+1)>>1); + else + finalY[j] = pred + (val>>1); + } else { + step2_flag[j] = 0; + finalY[j] = pred; + } + } + +#ifdef STB_VORBIS_NO_DEFER_FLOOR + do_floor(f, map, i, n, f->floor_buffers[i], finalY, step2_flag); +#else + // defer final floor computation until _after_ residue + for (j=0; j < g->values; ++j) { + if (!step2_flag[j]) + finalY[j] = -1; + } +#endif + } else { + error: + zero_channel[i] = TRUE; + } + // So we just defer everything else to later + + // at this point we've decoded the floor into buffer + } + } + CHECK(f); + // at this point we've decoded all floors + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + + // re-enable coupled channels if necessary + memcpy(really_zero_channel, zero_channel, sizeof(really_zero_channel[0]) * f->channels); + for (i=0; i < map->coupling_steps; ++i) + if (!zero_channel[map->chan[i].magnitude] || !zero_channel[map->chan[i].angle]) { + zero_channel[map->chan[i].magnitude] = zero_channel[map->chan[i].angle] = FALSE; + } + + CHECK(f); +// RESIDUE DECODE + for (i=0; i < map->submaps; ++i) { + float *residue_buffers[STB_VORBIS_MAX_CHANNELS]; + int r; + uint8 do_not_decode[256]; + int ch = 0; + for (j=0; j < f->channels; ++j) { + if (map->chan[j].mux == i) { + if (zero_channel[j]) { + do_not_decode[ch] = TRUE; + residue_buffers[ch] = NULL; + } else { + do_not_decode[ch] = FALSE; + residue_buffers[ch] = f->channel_buffers[j]; + } + ++ch; + } + } + r = map->submap_residue[i]; + decode_residue(f, residue_buffers, ch, n2, r, do_not_decode); + } + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + CHECK(f); + +// INVERSE COUPLING + for (i = map->coupling_steps-1; i >= 0; --i) { + int n2 = n >> 1; + float *m = f->channel_buffers[map->chan[i].magnitude]; + float *a = f->channel_buffers[map->chan[i].angle ]; + for (j=0; j < n2; ++j) { + float a2,m2; + if (m[j] > 0) + if (a[j] > 0) + m2 = m[j], a2 = m[j] - a[j]; + else + a2 = m[j], m2 = m[j] + a[j]; + else + if (a[j] > 0) + m2 = m[j], a2 = m[j] + a[j]; + else + a2 = m[j], m2 = m[j] - a[j]; + m[j] = m2; + a[j] = a2; + } + } + CHECK(f); + + // finish decoding the floors +#ifndef STB_VORBIS_NO_DEFER_FLOOR + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + do_floor(f, map, i, n, f->channel_buffers[i], f->finalY[i], NULL); + } + } +#else + for (i=0; i < f->channels; ++i) { + if (really_zero_channel[i]) { + memset(f->channel_buffers[i], 0, sizeof(*f->channel_buffers[i]) * n2); + } else { + for (j=0; j < n2; ++j) + f->channel_buffers[i][j] *= f->floor_buffers[i][j]; + } + } +#endif + +// INVERSE MDCT + CHECK(f); + for (i=0; i < f->channels; ++i) + inverse_mdct(f->channel_buffers[i], n, f, m->blockflag); + CHECK(f); + + // this shouldn't be necessary, unless we exited on an error + // and want to flush to get to the next packet + flush_packet(f); + + if (f->first_decode) { + // assume we start so first non-discarded sample is sample 0 + // this isn't to spec, but spec would require us to read ahead + // and decode the size of all current frames--could be done, + // but presumably it's not a commonly used feature + f->current_loc = -n2; // start of first frame is positioned for discard + // we might have to discard samples "from" the next frame too, + // if we're lapping a large block then a small at the start? + f->discard_samples_deferred = n - right_end; + f->current_loc_valid = TRUE; + f->first_decode = FALSE; + } else if (f->discard_samples_deferred) { + if (f->discard_samples_deferred >= right_start - left_start) { + f->discard_samples_deferred -= (right_start - left_start); + left_start = right_start; + *p_left = left_start; + } else { + left_start += f->discard_samples_deferred; + *p_left = left_start; + f->discard_samples_deferred = 0; + } + } else if (f->previous_length == 0 && f->current_loc_valid) { + // we're recovering from a seek... that means we're going to discard + // the samples from this packet even though we know our position from + // the last page header, so we need to update the position based on + // the discarded samples here + // but wait, the code below is going to add this in itself even + // on a discard, so we don't need to do it here... + } + + // check if we have ogg information about the sample # for this packet + if (f->last_seg_which == f->end_seg_with_known_loc) { + // if we have a valid current loc, and this is final: + if (f->current_loc_valid && (f->page_flag & PAGEFLAG_last_page)) { + uint32 current_end = f->known_loc_for_packet; + // then let's infer the size of the (probably) short final frame + if (current_end < f->current_loc + (right_end-left_start)) { + if (current_end < f->current_loc) { + // negative truncation, that's impossible! + *len = 0; + } else { + *len = current_end - f->current_loc; + } + *len += left_start; // this doesn't seem right, but has no ill effect on my test files + if (*len > right_end) *len = right_end; // this should never happen + f->current_loc += *len; + return TRUE; + } + } + // otherwise, just set our sample loc + // guess that the ogg granule pos refers to the _middle_ of the + // last frame? + // set f->current_loc to the position of left_start + f->current_loc = f->known_loc_for_packet - (n2-left_start); + f->current_loc_valid = TRUE; + } + if (f->current_loc_valid) + f->current_loc += (right_start - left_start); + + if (f->alloc.alloc_buffer) + assert(f->alloc.alloc_buffer_length_in_bytes == f->temp_offset); + *len = right_end; // ignore samples after the window goes to 0 + CHECK(f); + + return TRUE; +} + +static int vorbis_decode_packet(vorb *f, int *len, int *p_left, int *p_right) +{ + int mode, left_end, right_end; + if (!vorbis_decode_initial(f, p_left, &left_end, p_right, &right_end, &mode)) return 0; + return vorbis_decode_packet_rest(f, len, f->mode_config + mode, *p_left, left_end, *p_right, right_end, p_left); +} + +static int vorbis_finish_frame(stb_vorbis *f, int len, int left, int right) +{ + int prev,i,j; + // we use right&left (the start of the right- and left-window sin()-regions) + // to determine how much to return, rather than inferring from the rules + // (same result, clearer code); 'left' indicates where our sin() window + // starts, therefore where the previous window's right edge starts, and + // therefore where to start mixing from the previous buffer. 'right' + // indicates where our sin() ending-window starts, therefore that's where + // we start saving, and where our returned-data ends. + + // mixin from previous window + if (f->previous_length) { + int i,j, n = f->previous_length; + float *w = get_window(f, n); + for (i=0; i < f->channels; ++i) { + for (j=0; j < n; ++j) + f->channel_buffers[i][left+j] = + f->channel_buffers[i][left+j]*w[ j] + + f->previous_window[i][ j]*w[n-1-j]; + } + } + + prev = f->previous_length; + + // last half of this data becomes previous window + f->previous_length = len - right; + + // @OPTIMIZE: could avoid this copy by double-buffering the + // output (flipping previous_window with channel_buffers), but + // then previous_window would have to be 2x as large, and + // channel_buffers couldn't be temp mem (although they're NOT + // currently temp mem, they could be (unless we want to level + // performance by spreading out the computation)) + for (i=0; i < f->channels; ++i) + for (j=0; right+j < len; ++j) + f->previous_window[i][j] = f->channel_buffers[i][right+j]; + + if (!prev) + // there was no previous packet, so this data isn't valid... + // this isn't entirely true, only the would-have-overlapped data + // isn't valid, but this seems to be what the spec requires + return 0; + + // truncate a short frame + if (len < right) right = len; + + f->samples_output += right-left; + + return right - left; +} + +static int vorbis_pump_first_frame(stb_vorbis *f) +{ + int len, right, left, res; + res = vorbis_decode_packet(f, &len, &left, &right); + if (res) + vorbis_finish_frame(f, len, left, right); + return res; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API +static int is_whole_packet_present(stb_vorbis *f, int end_page) +{ + // make sure that we have the packet available before continuing... + // this requires a full ogg parse, but we know we can fetch from f->stream + + // instead of coding this out explicitly, we could save the current read state, + // read the next packet with get8() until end-of-packet, check f->eof, then + // reset the state? but that would be slower, esp. since we'd have over 256 bytes + // of state to restore (primarily the page segment table) + + int s = f->next_seg, first = TRUE; + uint8 *p = f->stream; + + if (s != -1) { // if we're not starting the packet with a 'continue on next page' flag + for (; s < f->segment_count; ++s) { + p += f->segments[s]; + if (f->segments[s] < 255) // stop at first short segment + break; + } + // either this continues, or it ends it... + if (end_page) + if (s < f->segment_count-1) return error(f, VORBIS_invalid_stream); + if (s == f->segment_count) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + for (; s == -1;) { + uint8 *q; + int n; + + // check that we have the page header ready + if (p + 26 >= f->stream_end) return error(f, VORBIS_need_more_data); + // validate the page + if (memcmp(p, ogg_page_header, 4)) return error(f, VORBIS_invalid_stream); + if (p[4] != 0) return error(f, VORBIS_invalid_stream); + if (first) { // the first segment must NOT have 'continued_packet', later ones MUST + if (f->previous_length) + if ((p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + // if no previous length, we're resynching, so we can come in on a continued-packet, + // which we'll just drop + } else { + if (!(p[5] & PAGEFLAG_continued_packet)) return error(f, VORBIS_invalid_stream); + } + n = p[26]; // segment counts + q = p+27; // q points to segment table + p = q + n; // advance past header + // make sure we've read the segment table + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + for (s=0; s < n; ++s) { + p += q[s]; + if (q[s] < 255) + break; + } + if (end_page) + if (s < n-1) return error(f, VORBIS_invalid_stream); + if (s == n) + s = -1; // set 'crosses page' flag + if (p > f->stream_end) return error(f, VORBIS_need_more_data); + first = FALSE; + } + return TRUE; +} +#endif // !STB_VORBIS_NO_PUSHDATA_API + +static int start_decoder(vorb *f) +{ + uint8 header[6], x,y; + int len,i,j,k, max_submaps = 0; + int longest_floorlist=0; + + // first page, first packet + + if (!start_page(f)) return FALSE; + // validate page flag + if (!(f->page_flag & PAGEFLAG_first_page)) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_last_page) return error(f, VORBIS_invalid_first_page); + if (f->page_flag & PAGEFLAG_continued_packet) return error(f, VORBIS_invalid_first_page); + // check for expected packet length + if (f->segment_count != 1) return error(f, VORBIS_invalid_first_page); + if (f->segments[0] != 30) return error(f, VORBIS_invalid_first_page); + // read packet + // check packet header + if (get8(f) != VORBIS_packet_id) return error(f, VORBIS_invalid_first_page); + if (!getn(f, header, 6)) return error(f, VORBIS_unexpected_eof); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_first_page); + // vorbis_version + if (get32(f) != 0) return error(f, VORBIS_invalid_first_page); + f->channels = get8(f); if (!f->channels) return error(f, VORBIS_invalid_first_page); + if (f->channels > STB_VORBIS_MAX_CHANNELS) return error(f, VORBIS_too_many_channels); + f->sample_rate = get32(f); if (!f->sample_rate) return error(f, VORBIS_invalid_first_page); + get32(f); // bitrate_maximum + get32(f); // bitrate_nominal + get32(f); // bitrate_minimum + x = get8(f); + { + int log0,log1; + log0 = x & 15; + log1 = x >> 4; + f->blocksize_0 = 1 << log0; + f->blocksize_1 = 1 << log1; + if (log0 < 6 || log0 > 13) return error(f, VORBIS_invalid_setup); + if (log1 < 6 || log1 > 13) return error(f, VORBIS_invalid_setup); + if (log0 > log1) return error(f, VORBIS_invalid_setup); + } + + // framing_flag + x = get8(f); + if (!(x & 1)) return error(f, VORBIS_invalid_first_page); + + // second packet! + if (!start_page(f)) return FALSE; + + if (!start_packet(f)) return FALSE; + do { + len = next_segment(f); + skip(f, len); + f->bytes_in_seg = 0; + } while (len); + + // third packet! + if (!start_packet(f)) return FALSE; + + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (IS_PUSH_MODE(f)) { + if (!is_whole_packet_present(f, TRUE)) { + // convert error in ogg header to write type + if (f->error == VORBIS_invalid_stream) + f->error = VORBIS_invalid_setup; + return FALSE; + } + } + #endif + + crc32_init(); // always init it, to avoid multithread race conditions + + if (get8_packet(f) != VORBIS_packet_setup) return error(f, VORBIS_invalid_setup); + for (i=0; i < 6; ++i) header[i] = get8_packet(f); + if (!vorbis_validate(header)) return error(f, VORBIS_invalid_setup); + + // codebooks + + f->codebook_count = get_bits(f,8) + 1; + f->codebooks = (Codebook *) setup_malloc(f, sizeof(*f->codebooks) * f->codebook_count); + if (f->codebooks == NULL) return error(f, VORBIS_outofmem); + memset(f->codebooks, 0, sizeof(*f->codebooks) * f->codebook_count); + for (i=0; i < f->codebook_count; ++i) { + uint32 *values; + int ordered, sorted_count; + int total=0; + uint8 *lengths; + Codebook *c = f->codebooks+i; + CHECK(f); + x = get_bits(f, 8); if (x != 0x42) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x43) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); if (x != 0x56) return error(f, VORBIS_invalid_setup); + x = get_bits(f, 8); + c->dimensions = (get_bits(f, 8)<<8) + x; + x = get_bits(f, 8); + y = get_bits(f, 8); + c->entries = (get_bits(f, 8)<<16) + (y<<8) + x; + ordered = get_bits(f,1); + c->sparse = ordered ? 0 : get_bits(f,1); + + if (c->dimensions == 0 && c->entries != 0) return error(f, VORBIS_invalid_setup); + + if (c->sparse) + lengths = (uint8 *) setup_temp_malloc(f, c->entries); + else + lengths = c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + + if (!lengths) return error(f, VORBIS_outofmem); + + if (ordered) { + int current_entry = 0; + int current_length = get_bits(f,5) + 1; + while (current_entry < c->entries) { + int limit = c->entries - current_entry; + int n = get_bits(f, ilog(limit)); + if (current_entry + n > (int) c->entries) { return error(f, VORBIS_invalid_setup); } + memset(lengths + current_entry, current_length, n); + current_entry += n; + ++current_length; + } + } else { + for (j=0; j < c->entries; ++j) { + int present = c->sparse ? get_bits(f,1) : 1; + if (present) { + lengths[j] = get_bits(f, 5) + 1; + ++total; + if (lengths[j] == 32) + return error(f, VORBIS_invalid_setup); + } else { + lengths[j] = NO_CODE; + } + } + } + + if (c->sparse && total >= c->entries >> 2) { + // convert sparse items to non-sparse! + if (c->entries > (int) f->setup_temp_memory_required) + f->setup_temp_memory_required = c->entries; + + c->codeword_lengths = (uint8 *) setup_malloc(f, c->entries); + if (c->codeword_lengths == NULL) return error(f, VORBIS_outofmem); + memcpy(c->codeword_lengths, lengths, c->entries); + setup_temp_free(f, lengths, c->entries); // note this is only safe if there have been no intervening temp mallocs! + lengths = c->codeword_lengths; + c->sparse = 0; + } + + // compute the size of the sorted tables + if (c->sparse) { + sorted_count = total; + } else { + sorted_count = 0; + #ifndef STB_VORBIS_NO_HUFFMAN_BINARY_SEARCH + for (j=0; j < c->entries; ++j) + if (lengths[j] > STB_VORBIS_FAST_HUFFMAN_LENGTH && lengths[j] != NO_CODE) + ++sorted_count; + #endif + } + + c->sorted_entries = sorted_count; + values = NULL; + + CHECK(f); + if (!c->sparse) { + c->codewords = (uint32 *) setup_malloc(f, sizeof(c->codewords[0]) * c->entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + } else { + unsigned int size; + if (c->sorted_entries) { + c->codeword_lengths = (uint8 *) setup_malloc(f, c->sorted_entries); + if (!c->codeword_lengths) return error(f, VORBIS_outofmem); + c->codewords = (uint32 *) setup_temp_malloc(f, sizeof(*c->codewords) * c->sorted_entries); + if (!c->codewords) return error(f, VORBIS_outofmem); + values = (uint32 *) setup_temp_malloc(f, sizeof(*values) * c->sorted_entries); + if (!values) return error(f, VORBIS_outofmem); + } + size = c->entries + (sizeof(*c->codewords) + sizeof(*values)) * c->sorted_entries; + if (size > f->setup_temp_memory_required) + f->setup_temp_memory_required = size; + } + + if (!compute_codewords(c, lengths, c->entries, values)) { + if (c->sparse) setup_temp_free(f, values, 0); + return error(f, VORBIS_invalid_setup); + } + + if (c->sorted_entries) { + // allocate an extra slot for sentinels + c->sorted_codewords = (uint32 *) setup_malloc(f, sizeof(*c->sorted_codewords) * (c->sorted_entries+1)); + if (c->sorted_codewords == NULL) return error(f, VORBIS_outofmem); + // allocate an extra slot at the front so that c->sorted_values[-1] is defined + // so that we can catch that case without an extra if + c->sorted_values = ( int *) setup_malloc(f, sizeof(*c->sorted_values ) * (c->sorted_entries+1)); + if (c->sorted_values == NULL) return error(f, VORBIS_outofmem); + ++c->sorted_values; + c->sorted_values[-1] = -1; + compute_sorted_huffman(c, lengths, values); + } + + if (c->sparse) { + setup_temp_free(f, values, sizeof(*values)*c->sorted_entries); + setup_temp_free(f, c->codewords, sizeof(*c->codewords)*c->sorted_entries); + setup_temp_free(f, lengths, c->entries); + c->codewords = NULL; + } + + compute_accelerated_huffman(c); + + CHECK(f); + c->lookup_type = get_bits(f, 4); + if (c->lookup_type > 2) return error(f, VORBIS_invalid_setup); + if (c->lookup_type > 0) { + uint16 *mults; + c->minimum_value = float32_unpack(get_bits(f, 32)); + c->delta_value = float32_unpack(get_bits(f, 32)); + c->value_bits = get_bits(f, 4)+1; + c->sequence_p = get_bits(f,1); + if (c->lookup_type == 1) { + c->lookup_values = lookup1_values(c->entries, c->dimensions); + } else { + c->lookup_values = c->entries * c->dimensions; + } + if (c->lookup_values == 0) return error(f, VORBIS_invalid_setup); + mults = (uint16 *) setup_temp_malloc(f, sizeof(mults[0]) * c->lookup_values); + if (mults == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < (int) c->lookup_values; ++j) { + int q = get_bits(f, c->value_bits); + if (q == EOP) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_invalid_setup); } + mults[j] = q; + } + +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + if (c->lookup_type == 1) { + int len, sparse = c->sparse; + float last=0; + // pre-expand the lookup1-style multiplicands, to avoid a divide in the inner loop + if (sparse) { + if (c->sorted_entries == 0) goto skip; + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->sorted_entries * c->dimensions); + } else + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->entries * c->dimensions); + if (c->multiplicands == NULL) { setup_temp_free(f,mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + len = sparse ? c->sorted_entries : c->entries; + for (j=0; j < len; ++j) { + unsigned int z = sparse ? c->sorted_values[j] : j; + unsigned int div=1; + for (k=0; k < c->dimensions; ++k) { + int off = (z / div) % c->lookup_values; + float val = mults[off]; + val = mults[off]*c->delta_value + c->minimum_value + last; + c->multiplicands[j*c->dimensions + k] = val; + if (c->sequence_p) + last = val; + if (k+1 < c->dimensions) { + if (div > UINT_MAX / (unsigned int) c->lookup_values) { + setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); + return error(f, VORBIS_invalid_setup); + } + div *= c->lookup_values; + } + } + } + c->lookup_type = 2; + } + else +#endif + { + float last=0; + CHECK(f); + c->multiplicands = (codetype *) setup_malloc(f, sizeof(c->multiplicands[0]) * c->lookup_values); + if (c->multiplicands == NULL) { setup_temp_free(f, mults,sizeof(mults[0])*c->lookup_values); return error(f, VORBIS_outofmem); } + for (j=0; j < (int) c->lookup_values; ++j) { + float val = mults[j] * c->delta_value + c->minimum_value + last; + c->multiplicands[j] = val; + if (c->sequence_p) + last = val; + } + } +#ifndef STB_VORBIS_DIVIDES_IN_CODEBOOK + skip:; +#endif + setup_temp_free(f, mults, sizeof(mults[0])*c->lookup_values); + + CHECK(f); + } + CHECK(f); + } + + // time domain transfers (notused) + + x = get_bits(f, 6) + 1; + for (i=0; i < x; ++i) { + uint32 z = get_bits(f, 16); + if (z != 0) return error(f, VORBIS_invalid_setup); + } + + // Floors + f->floor_count = get_bits(f, 6)+1; + f->floor_config = (Floor *) setup_malloc(f, f->floor_count * sizeof(*f->floor_config)); + if (f->floor_config == NULL) return error(f, VORBIS_outofmem); + for (i=0; i < f->floor_count; ++i) { + f->floor_types[i] = get_bits(f, 16); + if (f->floor_types[i] > 1) return error(f, VORBIS_invalid_setup); + if (f->floor_types[i] == 0) { + Floor0 *g = &f->floor_config[i].floor0; + g->order = get_bits(f,8); + g->rate = get_bits(f,16); + g->bark_map_size = get_bits(f,16); + g->amplitude_bits = get_bits(f,6); + g->amplitude_offset = get_bits(f,8); + g->number_of_books = get_bits(f,4) + 1; + for (j=0; j < g->number_of_books; ++j) + g->book_list[j] = get_bits(f,8); + return error(f, VORBIS_feature_not_supported); + } else { + stbv__floor_ordering p[31*8+2]; + Floor1 *g = &f->floor_config[i].floor1; + int max_class = -1; + g->partitions = get_bits(f, 5); + for (j=0; j < g->partitions; ++j) { + g->partition_class_list[j] = get_bits(f, 4); + if (g->partition_class_list[j] > max_class) + max_class = g->partition_class_list[j]; + } + for (j=0; j <= max_class; ++j) { + g->class_dimensions[j] = get_bits(f, 3)+1; + g->class_subclasses[j] = get_bits(f, 2); + if (g->class_subclasses[j]) { + g->class_masterbooks[j] = get_bits(f, 8); + if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + for (k=0; k < 1 << g->class_subclasses[j]; ++k) { + g->subclass_books[j][k] = get_bits(f,8)-1; + if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } + } + g->floor1_multiplier = get_bits(f,2)+1; + g->rangebits = get_bits(f,4); + g->Xlist[0] = 0; + g->Xlist[1] = 1 << g->rangebits; + g->values = 2; + for (j=0; j < g->partitions; ++j) { + int c = g->partition_class_list[j]; + for (k=0; k < g->class_dimensions[c]; ++k) { + g->Xlist[g->values] = get_bits(f, g->rangebits); + ++g->values; + } + } + // precompute the sorting + for (j=0; j < g->values; ++j) { + p[j].x = g->Xlist[j]; + p[j].id = j; + } + qsort(p, g->values, sizeof(p[0]), point_compare); + for (j=0; j < g->values; ++j) + g->sorted_order[j] = (uint8) p[j].id; + // precompute the neighbors + for (j=2; j < g->values; ++j) { + int low,hi; + neighbors(g->Xlist, j, &low,&hi); + g->neighbors[j][0] = low; + g->neighbors[j][1] = hi; + } + + if (g->values > longest_floorlist) + longest_floorlist = g->values; + } + } + + // Residue + f->residue_count = get_bits(f, 6)+1; + f->residue_config = (Residue *) setup_malloc(f, f->residue_count * sizeof(f->residue_config[0])); + if (f->residue_config == NULL) return error(f, VORBIS_outofmem); + memset(f->residue_config, 0, f->residue_count * sizeof(f->residue_config[0])); + for (i=0; i < f->residue_count; ++i) { + uint8 residue_cascade[64]; + Residue *r = f->residue_config+i; + f->residue_types[i] = get_bits(f, 16); + if (f->residue_types[i] > 2) return error(f, VORBIS_invalid_setup); + r->begin = get_bits(f, 24); + r->end = get_bits(f, 24); + if (r->end < r->begin) return error(f, VORBIS_invalid_setup); + r->part_size = get_bits(f,24)+1; + r->classifications = get_bits(f,6)+1; + r->classbook = get_bits(f,8); + if (r->classbook >= f->codebook_count) return error(f, VORBIS_invalid_setup); + for (j=0; j < r->classifications; ++j) { + uint8 high_bits=0; + uint8 low_bits=get_bits(f,3); + if (get_bits(f,1)) + high_bits = get_bits(f,5); + residue_cascade[j] = high_bits*8 + low_bits; + } + r->residue_books = (short (*)[8]) setup_malloc(f, sizeof(r->residue_books[0]) * r->classifications); + if (r->residue_books == NULL) return error(f, VORBIS_outofmem); + for (j=0; j < r->classifications; ++j) { + for (k=0; k < 8; ++k) { + if (residue_cascade[j] & (1 << k)) { + r->residue_books[j][k] = get_bits(f, 8); + if (r->residue_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); + } else { + r->residue_books[j][k] = -1; + } + } + } + // precompute the classifications[] array to avoid inner-loop mod/divide + // call it 'classdata' since we already have r->classifications + r->classdata = (uint8 **) setup_malloc(f, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + if (!r->classdata) return error(f, VORBIS_outofmem); + memset(r->classdata, 0, sizeof(*r->classdata) * f->codebooks[r->classbook].entries); + for (j=0; j < f->codebooks[r->classbook].entries; ++j) { + int classwords = f->codebooks[r->classbook].dimensions; + int temp = j; + r->classdata[j] = (uint8 *) setup_malloc(f, sizeof(r->classdata[j][0]) * classwords); + if (r->classdata[j] == NULL) return error(f, VORBIS_outofmem); + for (k=classwords-1; k >= 0; --k) { + r->classdata[j][k] = temp % r->classifications; + temp /= r->classifications; + } + } + } + + f->mapping_count = get_bits(f,6)+1; + f->mapping = (Mapping *) setup_malloc(f, f->mapping_count * sizeof(*f->mapping)); + if (f->mapping == NULL) return error(f, VORBIS_outofmem); + memset(f->mapping, 0, f->mapping_count * sizeof(*f->mapping)); + for (i=0; i < f->mapping_count; ++i) { + Mapping *m = f->mapping + i; + int mapping_type = get_bits(f,16); + if (mapping_type != 0) return error(f, VORBIS_invalid_setup); + m->chan = (MappingChannel *) setup_malloc(f, f->channels * sizeof(*m->chan)); + if (m->chan == NULL) return error(f, VORBIS_outofmem); + if (get_bits(f,1)) + m->submaps = get_bits(f,4)+1; + else + m->submaps = 1; + if (m->submaps > max_submaps) + max_submaps = m->submaps; + if (get_bits(f,1)) { + m->coupling_steps = get_bits(f,8)+1; + for (k=0; k < m->coupling_steps; ++k) { + m->chan[k].magnitude = get_bits(f, ilog(f->channels-1)); + m->chan[k].angle = get_bits(f, ilog(f->channels-1)); + if (m->chan[k].magnitude >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].angle >= f->channels) return error(f, VORBIS_invalid_setup); + if (m->chan[k].magnitude == m->chan[k].angle) return error(f, VORBIS_invalid_setup); + } + } else + m->coupling_steps = 0; + + // reserved field + if (get_bits(f,2)) return error(f, VORBIS_invalid_setup); + if (m->submaps > 1) { + for (j=0; j < f->channels; ++j) { + m->chan[j].mux = get_bits(f, 4); + if (m->chan[j].mux >= m->submaps) return error(f, VORBIS_invalid_setup); + } + } else + // @SPECIFICATION: this case is missing from the spec + for (j=0; j < f->channels; ++j) + m->chan[j].mux = 0; + + for (j=0; j < m->submaps; ++j) { + get_bits(f,8); // discard + m->submap_floor[j] = get_bits(f,8); + m->submap_residue[j] = get_bits(f,8); + if (m->submap_floor[j] >= f->floor_count) return error(f, VORBIS_invalid_setup); + if (m->submap_residue[j] >= f->residue_count) return error(f, VORBIS_invalid_setup); + } + } + + // Modes + f->mode_count = get_bits(f, 6)+1; + for (i=0; i < f->mode_count; ++i) { + Mode *m = f->mode_config+i; + m->blockflag = get_bits(f,1); + m->windowtype = get_bits(f,16); + m->transformtype = get_bits(f,16); + m->mapping = get_bits(f,8); + if (m->windowtype != 0) return error(f, VORBIS_invalid_setup); + if (m->transformtype != 0) return error(f, VORBIS_invalid_setup); + if (m->mapping >= f->mapping_count) return error(f, VORBIS_invalid_setup); + } + + flush_packet(f); + + f->previous_length = 0; + + for (i=0; i < f->channels; ++i) { + f->channel_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1); + f->previous_window[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + f->finalY[i] = (int16 *) setup_malloc(f, sizeof(int16) * longest_floorlist); + if (f->channel_buffers[i] == NULL || f->previous_window[i] == NULL || f->finalY[i] == NULL) return error(f, VORBIS_outofmem); + memset(f->channel_buffers[i], 0, sizeof(float) * f->blocksize_1); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + f->floor_buffers[i] = (float *) setup_malloc(f, sizeof(float) * f->blocksize_1/2); + if (f->floor_buffers[i] == NULL) return error(f, VORBIS_outofmem); + #endif + } + + if (!init_blocksize(f, 0, f->blocksize_0)) return FALSE; + if (!init_blocksize(f, 1, f->blocksize_1)) return FALSE; + f->blocksize[0] = f->blocksize_0; + f->blocksize[1] = f->blocksize_1; + +#ifdef STB_VORBIS_DIVIDE_TABLE + if (integer_divide_table[1][1]==0) + for (i=0; i < DIVTAB_NUMER; ++i) + for (j=1; j < DIVTAB_DENOM; ++j) + integer_divide_table[i][j] = i / j; +#endif + + // compute how much temporary memory is needed + + // 1. + { + uint32 imdct_mem = (f->blocksize_1 * sizeof(float) >> 1); + uint32 classify_mem; + int i,max_part_read=0; + for (i=0; i < f->residue_count; ++i) { + Residue *r = f->residue_config + i; + unsigned int actual_size = f->blocksize_1 / 2; + unsigned int limit_r_begin = r->begin < actual_size ? r->begin : actual_size; + unsigned int limit_r_end = r->end < actual_size ? r->end : actual_size; + int n_read = limit_r_end - limit_r_begin; + int part_read = n_read / r->part_size; + if (part_read > max_part_read) + max_part_read = part_read; + } + #ifndef STB_VORBIS_DIVIDES_IN_RESIDUE + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(uint8 *)); + #else + classify_mem = f->channels * (sizeof(void*) + max_part_read * sizeof(int *)); + #endif + + // maximum reasonable partition size is f->blocksize_1 + + f->temp_memory_required = classify_mem; + if (imdct_mem > f->temp_memory_required) + f->temp_memory_required = imdct_mem; + } + + f->first_decode = TRUE; + + if (f->alloc.alloc_buffer) { + assert(f->temp_offset == f->alloc.alloc_buffer_length_in_bytes); + // check if there's enough temp memory so we don't error later + if (f->setup_offset + sizeof(*f) + f->temp_memory_required > (unsigned) f->temp_offset) + return error(f, VORBIS_outofmem); + } + + f->first_audio_page_offset = stb_vorbis_get_file_offset(f); + + return TRUE; +} + +static void vorbis_deinit(stb_vorbis *p) +{ + int i,j; + if (p->residue_config) { + for (i=0; i < p->residue_count; ++i) { + Residue *r = p->residue_config+i; + if (r->classdata) { + for (j=0; j < p->codebooks[r->classbook].entries; ++j) + setup_free(p, r->classdata[j]); + setup_free(p, r->classdata); + } + setup_free(p, r->residue_books); + } + } + + if (p->codebooks) { + CHECK(p); + for (i=0; i < p->codebook_count; ++i) { + Codebook *c = p->codebooks + i; + setup_free(p, c->codeword_lengths); + setup_free(p, c->multiplicands); + setup_free(p, c->codewords); + setup_free(p, c->sorted_codewords); + // c->sorted_values[-1] is the first entry in the array + setup_free(p, c->sorted_values ? c->sorted_values-1 : NULL); + } + setup_free(p, p->codebooks); + } + setup_free(p, p->floor_config); + setup_free(p, p->residue_config); + if (p->mapping) { + for (i=0; i < p->mapping_count; ++i) + setup_free(p, p->mapping[i].chan); + setup_free(p, p->mapping); + } + CHECK(p); + for (i=0; i < p->channels && i < STB_VORBIS_MAX_CHANNELS; ++i) { + setup_free(p, p->channel_buffers[i]); + setup_free(p, p->previous_window[i]); + #ifdef STB_VORBIS_NO_DEFER_FLOOR + setup_free(p, p->floor_buffers[i]); + #endif + setup_free(p, p->finalY[i]); + } + for (i=0; i < 2; ++i) { + setup_free(p, p->A[i]); + setup_free(p, p->B[i]); + setup_free(p, p->C[i]); + setup_free(p, p->window[i]); + setup_free(p, p->bit_reverse[i]); + } + #ifdef __SDL_SOUND_INTERNAL__ + if (p->close_on_free) SDL_RWclose(p->rwops); + #endif + #ifndef STB_VORBIS_NO_STDIO + if (p->close_on_free) fclose(p->f); + #endif +} + +void stb_vorbis_close(stb_vorbis *p) +{ + if (p == NULL) return; + vorbis_deinit(p); + setup_free(p,p); +} + +static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) +{ + memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start + if (z) { + p->alloc = *z; + p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; + } + p->eof = 0; + p->error = VORBIS__no_error; + p->stream = NULL; + p->codebooks = NULL; + p->page_crc_tests = -1; + #ifdef __SDL_SOUND_INTERNAL__ + p->close_on_free = FALSE; + p->rwops = NULL; + #endif + #ifndef STB_VORBIS_NO_STDIO + p->close_on_free = FALSE; + p->f = NULL; + #endif +} + +int stb_vorbis_get_sample_offset(stb_vorbis *f) +{ + if (f->current_loc_valid) + return f->current_loc; + else + return -1; +} + +stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f) +{ + stb_vorbis_info d; + d.channels = f->channels; + d.sample_rate = f->sample_rate; + d.setup_memory_required = f->setup_memory_required; + d.setup_temp_memory_required = f->setup_temp_memory_required; + d.temp_memory_required = f->temp_memory_required; + d.max_frame_size = f->blocksize_1 >> 1; + return d; +} + +int stb_vorbis_get_error(stb_vorbis *f) +{ + int e = f->error; + f->error = VORBIS__no_error; + return e; +} + +static stb_vorbis * vorbis_alloc(stb_vorbis *f) +{ + stb_vorbis *p = (stb_vorbis *) setup_malloc(f, sizeof(*p)); + return p; +} + +#ifndef STB_VORBIS_NO_PUSHDATA_API + +void stb_vorbis_flush_pushdata(stb_vorbis *f) +{ + f->previous_length = 0; + f->page_crc_tests = 0; + f->discard_samples_deferred = 0; + f->current_loc_valid = FALSE; + f->first_decode = FALSE; + f->samples_output = 0; + f->channel_buffer_start = 0; + f->channel_buffer_end = 0; +} + +static int vorbis_search_for_page_pushdata(vorb *f, uint8 *data, int data_len) +{ + int i,n; + for (i=0; i < f->page_crc_tests; ++i) + f->scan[i].bytes_done = 0; + + // if we have room for more scans, search for them first, because + // they may cause us to stop early if their header is incomplete + if (f->page_crc_tests < STB_VORBIS_PUSHDATA_CRC_COUNT) { + if (data_len < 4) return 0; + data_len -= 3; // need to look for 4-byte sequence, so don't miss + // one that straddles a boundary + for (i=0; i < data_len; ++i) { + if (data[i] == 0x4f) { + if (0==memcmp(data+i, ogg_page_header, 4)) { + int j,len; + uint32 crc; + // make sure we have the whole page header + if (i+26 >= data_len || i+27+data[i+26] >= data_len) { + // only read up to this page start, so hopefully we'll + // have the whole page header start next time + data_len = i; + break; + } + // ok, we have it all; compute the length of the page + len = 27 + data[i+26]; + for (j=0; j < data[i+26]; ++j) + len += data[i+27+j]; + // scan everything up to the embedded crc (which we must 0) + crc = 0; + for (j=0; j < 22; ++j) + crc = crc32_update(crc, data[i+j]); + // now process 4 0-bytes + for ( ; j < 26; ++j) + crc = crc32_update(crc, 0); + // len is the total number of bytes we need to scan + n = f->page_crc_tests++; + f->scan[n].bytes_left = len-j; + f->scan[n].crc_so_far = crc; + f->scan[n].goal_crc = data[i+22] + (data[i+23] << 8) + (data[i+24]<<16) + (data[i+25]<<24); + // if the last frame on a page is continued to the next, then + // we can't recover the sample_loc immediately + if (data[i+27+data[i+26]-1] == 255) + f->scan[n].sample_loc = ~0; + else + f->scan[n].sample_loc = data[i+6] + (data[i+7] << 8) + (data[i+ 8]<<16) + (data[i+ 9]<<24); + f->scan[n].bytes_done = i+j; + if (f->page_crc_tests == STB_VORBIS_PUSHDATA_CRC_COUNT) + break; + // keep going if we still have room for more + } + } + } + } + + for (i=0; i < f->page_crc_tests;) { + uint32 crc; + int j; + int n = f->scan[i].bytes_done; + int m = f->scan[i].bytes_left; + if (m > data_len - n) m = data_len - n; + // m is the bytes to scan in the current chunk + crc = f->scan[i].crc_so_far; + for (j=0; j < m; ++j) + crc = crc32_update(crc, data[n+j]); + f->scan[i].bytes_left -= m; + f->scan[i].crc_so_far = crc; + if (f->scan[i].bytes_left == 0) { + // does it match? + if (f->scan[i].crc_so_far == f->scan[i].goal_crc) { + // Houston, we have page + data_len = n+m; // consumption amount is wherever that scan ended + f->page_crc_tests = -1; // drop out of page scan mode + f->previous_length = 0; // decode-but-don't-output one frame + f->next_seg = -1; // start a new page + f->current_loc = f->scan[i].sample_loc; // set the current sample location + // to the amount we'd have decoded had we decoded this page + f->current_loc_valid = f->current_loc != ~0U; + return data_len; + } + // delete entry + f->scan[i] = f->scan[--f->page_crc_tests]; + } else { + ++i; + } + } + + return data_len; +} + +// return value: number of bytes we used +int stb_vorbis_decode_frame_pushdata( + stb_vorbis *f, // the file we're decoding + const uint8 *data, int data_len, // the memory available for decoding + int *channels, // place to write number of float * buffers + float ***output, // place to write float ** array of float * buffers + int *samples // place to write number of output samples + ) +{ + int i; + int len,right,left; + + if (!IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (f->page_crc_tests >= 0) { + *samples = 0; + return vorbis_search_for_page_pushdata(f, (uint8 *) data, data_len); + } + + f->stream = (uint8 *) data; + f->stream_end = (uint8 *) data + data_len; + f->error = VORBIS__no_error; + + // check that we have the entire packet in memory + if (!is_whole_packet_present(f, FALSE)) { + *samples = 0; + return 0; + } + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + // save the actual error we encountered + enum STBVorbisError error = f->error; + if (error == VORBIS_bad_packet_type) { + // flush and resynch + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + if (error == VORBIS_continued_packet_flag_invalid) { + if (f->previous_length == 0) { + // we may be resynching, in which case it's ok to hit one + // of these; just discard the packet + f->error = VORBIS__no_error; + while (get8_packet(f) != EOP) + if (f->eof) break; + *samples = 0; + return (int) (f->stream - data); + } + } + // if we get an error while parsing, what to do? + // well, it DEFINITELY won't work to continue from where we are! + stb_vorbis_flush_pushdata(f); + // restore the error that actually made us bail + f->error = error; + *samples = 0; + return 1; + } + + // success! + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + if (channels) *channels = f->channels; + *samples = len; + *output = f->outputs; + return (int) (f->stream - data); +} + +stb_vorbis *stb_vorbis_open_pushdata( + const unsigned char *data, int data_len, // the memory available for decoding + int *data_used, // only defined if result is not NULL + int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + data_len; + p.push_mode = TRUE; + if (!start_decoder(&p)) { + if (p.eof) + *error = VORBIS_need_more_data; + else + *error = p.error; + return NULL; + } + f = vorbis_alloc(&p); + if (f) { + *f = p; + *data_used = (int) (f->stream - data); + *error = 0; + return f; + } else { + vorbis_deinit(&p); + return NULL; + } +} +#endif // STB_VORBIS_NO_PUSHDATA_API + +unsigned int stb_vorbis_get_file_offset(stb_vorbis *f) +{ + #ifndef STB_VORBIS_NO_PUSHDATA_API + if (f->push_mode) return 0; + #endif + if (USE_MEMORY(f)) return (unsigned int) (f->stream - f->stream_start); + #ifdef __SDL_SOUND_INTERNAL__ + return (unsigned int) (SDL_RWtell(f->rwops) - f->rwops_start); + #endif + #ifndef STB_VORBIS_NO_STDIO + return (unsigned int) (ftell(f->f) - f->f_start); + #endif +} + +#ifndef STB_VORBIS_NO_PULLDATA_API +// +// DATA-PULLING API +// + +static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) +{ + for(;;) { + int n; + if (f->eof) return 0; + n = get8(f); + if (n == 0x4f) { // page header candidate + unsigned int retry_loc = stb_vorbis_get_file_offset(f); + int i; + // check if we're off the end of a file_section stream + if (retry_loc - 25 > f->stream_len) + return 0; + // check the rest of the header + for (i=1; i < 4; ++i) + if (get8(f) != ogg_page_header[i]) + break; + if (f->eof) return 0; + if (i == 4) { + uint8 header[27]; + uint32 i, crc, goal, len; + for (i=0; i < 4; ++i) + header[i] = ogg_page_header[i]; + for (; i < 27; ++i) + header[i] = get8(f); + if (f->eof) return 0; + if (header[4] != 0) goto invalid; + goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + for (i=22; i < 26; ++i) + header[i] = 0; + crc = 0; + for (i=0; i < 27; ++i) + crc = crc32_update(crc, header[i]); + len = 0; + for (i=0; i < header[26]; ++i) { + int s = get8(f); + crc = crc32_update(crc, s); + len += s; + } + if (len && f->eof) return 0; + for (i=0; i < len; ++i) + crc = crc32_update(crc, get8(f)); + // finished parsing probable page + if (crc == goal) { + // we could now check that it's either got the last + // page flag set, OR it's followed by the capture + // pattern, but I guess TECHNICALLY you could have + // a file with garbage between each ogg page and recover + // from it automatically? So even though that paranoia + // might decrease the chance of an invalid decode by + // another 2^32, not worth it since it would hose those + // invalid-but-useful files? + if (end) + *end = stb_vorbis_get_file_offset(f); + if (last) { + if (header[5] & 0x04) + *last = 1; + else + *last = 0; + } + set_file_offset(f, retry_loc-1); + return 1; + } + } + invalid: + // not a valid page, so rewind and look for next one + set_file_offset(f, retry_loc); + } + } +} + + +#define SAMPLE_unknown 0xffffffff + +// seeking is implemented with a binary search, which narrows down the range to +// 64K, before using a linear search (because finding the synchronization +// pattern can be expensive, and the chance we'd find the end page again is +// relatively high for small ranges) +// +// two initial interpolation-style probes are used at the start of the search +// to try to bound either side of the binary search sensibly, while still +// working in O(log n) time if they fail. + +static int get_seek_page_info(stb_vorbis *f, ProbedPage *z) +{ + uint8 header[27], lacing[255]; + int i,len; + + // record where the page starts + z->page_start = stb_vorbis_get_file_offset(f); + + // parse the header + getn(f, header, 27); + if (header[0] != 'O' || header[1] != 'g' || header[2] != 'g' || header[3] != 'S') + return 0; + getn(f, lacing, header[26]); + + // determine the length of the payload + len = 0; + for (i=0; i < header[26]; ++i) + len += lacing[i]; + + // this implies where the page ends + z->page_end = z->page_start + 27 + header[26] + len; + + // read the last-decoded sample out of the data + z->last_decoded_sample = header[6] + (header[7] << 8) + (header[8] << 16) + (header[9] << 24); + + // restore file state to where we were + set_file_offset(f, z->page_start); + return 1; +} + +// rarely used function to seek back to the preceeding page while finding the +// start of a packet +static int go_to_page_before(stb_vorbis *f, unsigned int limit_offset) +{ + unsigned int previous_safe, end; + + // now we want to seek back 64K from the limit + if (limit_offset >= 65536 && limit_offset-65536 >= f->first_audio_page_offset) + previous_safe = limit_offset - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + + while (vorbis_find_page(f, &end, NULL)) { + if (end >= limit_offset && stb_vorbis_get_file_offset(f) < limit_offset) + return 1; + set_file_offset(f, end); + } + + return 0; +} + +// implements the search logic for finding a page and starting decoding. if +// the function succeeds, current_loc_valid will be true and current_loc will +// be less than or equal to the provided sample number (the closer the +// better). +static int seek_to_sample_coarse(stb_vorbis *f, uint32 sample_number) +{ + ProbedPage left, right, mid; + int i, start_seg_with_known_loc, end_pos, page_start; + uint32 delta, stream_length, padding; + double offset, bytes_per_sample; + int probe = 0; + + // find the last page and validate the target sample + stream_length = stb_vorbis_stream_length_in_samples(f); + if (stream_length == 0) return error(f, VORBIS_seek_without_length); + if (sample_number > stream_length) return error(f, VORBIS_seek_invalid); + + // this is the maximum difference between the window-center (which is the + // actual granule position value), and the right-start (which the spec + // indicates should be the granule position (give or take one)). + padding = ((f->blocksize_1 - f->blocksize_0) >> 2); + if (sample_number < padding) + sample_number = 0; + else + sample_number -= padding; + + left = f->p_first; + while (left.last_decoded_sample == ~0U) { + // (untested) the first page does not have a 'last_decoded_sample' + set_file_offset(f, left.page_end); + if (!get_seek_page_info(f, &left)) goto error; + } + + right = f->p_last; + assert(right.last_decoded_sample != ~0U); + + // starting from the start is handled differently + if (sample_number <= left.last_decoded_sample) { + if (stb_vorbis_seek_start(f)) + return 1; + return 0; + } + + while (left.page_end != right.page_start) { + assert(left.page_end < right.page_start); + // search range in bytes + delta = right.page_start - left.page_end; + if (delta <= 65536) { + // there's only 64K left to search - handle it linearly + set_file_offset(f, left.page_end); + } else { + if (probe < 2) { + if (probe == 0) { + // first probe (interpolate) + double data_bytes = right.page_end - left.page_start; + bytes_per_sample = data_bytes / right.last_decoded_sample; + offset = left.page_start + bytes_per_sample * (sample_number - left.last_decoded_sample); + } else { + // second probe (try to bound the other side) + double error = ((double) sample_number - mid.last_decoded_sample) * bytes_per_sample; + if (error >= 0 && error < 8000) error = 8000; + if (error < 0 && error > -8000) error = -8000; + offset += error * 2; + } + + // ensure the offset is valid + if (offset < left.page_end) + offset = left.page_end; + if (offset > right.page_start - 65536) + offset = right.page_start - 65536; + + set_file_offset(f, (unsigned int) offset); + } else { + // binary search for large ranges (offset by 32K to ensure + // we don't hit the right page) + set_file_offset(f, left.page_end + (delta / 2) - 32768); + } + + if (!vorbis_find_page(f, NULL, NULL)) goto error; + } + + for (;;) { + if (!get_seek_page_info(f, &mid)) goto error; + if (mid.last_decoded_sample != ~0U) break; + // (untested) no frames end on this page + set_file_offset(f, mid.page_end); + assert(mid.page_start < right.page_start); + } + + // if we've just found the last page again then we're in a tricky file, + // and we're close enough. + if (mid.page_start == right.page_start) + break; + + if (sample_number < mid.last_decoded_sample) + right = mid; + else + left = mid; + + ++probe; + } + + // seek back to start of the last packet + page_start = left.page_start; + set_file_offset(f, page_start); + if (!start_page(f)) return error(f, VORBIS_seek_failed); + end_pos = f->end_seg_with_known_loc; + assert(end_pos >= 0); + + for (;;) { + for (i = end_pos; i > 0; --i) + if (f->segments[i-1] != 255) + break; + + start_seg_with_known_loc = i; + + if (start_seg_with_known_loc > 0 || !(f->page_flag & PAGEFLAG_continued_packet)) + break; + + // (untested) the final packet begins on an earlier page + if (!go_to_page_before(f, page_start)) + goto error; + + page_start = stb_vorbis_get_file_offset(f); + if (!start_page(f)) goto error; + end_pos = f->segment_count - 1; + } + + // prepare to start decoding + f->current_loc_valid = FALSE; + f->last_seg = FALSE; + f->valid_bits = 0; + f->packet_bytes = 0; + f->bytes_in_seg = 0; + f->previous_length = 0; + f->next_seg = start_seg_with_known_loc; + + for (i = 0; i < start_seg_with_known_loc; i++) + skip(f, f->segments[i]); + + // start decoding (optimizable - this frame is generally discarded) + if (!vorbis_pump_first_frame(f)) + return 0; + if (f->current_loc > sample_number) + return error(f, VORBIS_seek_failed); + return 1; + +error: + // try to restore the file to a valid state + stb_vorbis_seek_start(f); + return error(f, VORBIS_seek_failed); +} + +// the same as vorbis_decode_initial, but without advancing +static int peek_decode_initial(vorb *f, int *p_left_start, int *p_left_end, int *p_right_start, int *p_right_end, int *mode) +{ + int bits_read, bytes_read; + + if (!vorbis_decode_initial(f, p_left_start, p_left_end, p_right_start, p_right_end, mode)) + return 0; + + // either 1 or 2 bytes were read, figure out which so we can rewind + bits_read = 1 + ilog(f->mode_count-1); + if (f->mode_config[*mode].blockflag) + bits_read += 2; + bytes_read = (bits_read + 7) / 8; + + f->bytes_in_seg += bytes_read; + f->packet_bytes -= bytes_read; + skip(f, -bytes_read); + if (f->next_seg == -1) + f->next_seg = f->segment_count - 1; + else + f->next_seg--; + f->valid_bits = 0; + + return 1; +} + +int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number) +{ + uint32 max_frame_samples; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + // fast page-level search + if (!seek_to_sample_coarse(f, sample_number)) + return 0; + + assert(f->current_loc_valid); + assert(f->current_loc <= sample_number); + + // linear search for the relevant packet + max_frame_samples = (f->blocksize_1*3 - f->blocksize_0) >> 2; + while (f->current_loc < sample_number) { + int left_start, left_end, right_start, right_end, mode, frame_samples; + if (!peek_decode_initial(f, &left_start, &left_end, &right_start, &right_end, &mode)) + return error(f, VORBIS_seek_failed); + // calculate the number of samples returned by the next frame + frame_samples = right_start - left_start; + if (f->current_loc + frame_samples > sample_number) { + return 1; // the next frame will contain the sample + } else if (f->current_loc + frame_samples + max_frame_samples > sample_number) { + // there's a chance the frame after this could contain the sample + vorbis_pump_first_frame(f); + } else { + // this frame is too early to be relevant + f->current_loc += frame_samples; + f->previous_length = 0; + maybe_start_packet(f); + flush_packet(f); + } + } + // the next frame will start with the sample + assert(f->current_loc == sample_number); + return 1; +} + +int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number) +{ + if (!stb_vorbis_seek_frame(f, sample_number)) + return 0; + + if (sample_number != f->current_loc) { + int n; + uint32 frame_start = f->current_loc; + stb_vorbis_get_frame_float(f, &n, NULL); + assert(sample_number > frame_start); + assert(f->channel_buffer_start + (int) (sample_number-frame_start) <= f->channel_buffer_end); + f->channel_buffer_start += (sample_number - frame_start); + } + + return 1; +} + +int stb_vorbis_seek_start(stb_vorbis *f) +{ + if (IS_PUSH_MODE(f)) { return error(f, VORBIS_invalid_api_mixing); } + set_file_offset(f, f->first_audio_page_offset); + f->previous_length = 0; + f->first_decode = TRUE; + f->next_seg = -1; + return vorbis_pump_first_frame(f); +} + +unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) +{ + unsigned int restore_offset, previous_safe; + unsigned int end, last_page_loc; + + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + if (!f->total_samples) { + unsigned int last; + uint32 lo,hi; + char header[6]; + + // first, store the current decode position so we can restore it + restore_offset = stb_vorbis_get_file_offset(f); + + // now we want to seek back 64K from the end (the last page must + // be at most a little less than 64K, but let's allow a little slop) + if (f->stream_len >= 65536 && f->stream_len-65536 >= f->first_audio_page_offset) + previous_safe = f->stream_len - 65536; + else + previous_safe = f->first_audio_page_offset; + + set_file_offset(f, previous_safe); + // previous_safe is now our candidate 'earliest known place that seeking + // to will lead to the final page' + + if (!vorbis_find_page(f, &end, &last)) { + // if we can't find a page, we're hosed! + f->error = VORBIS_cant_find_last_page; + f->total_samples = 0xffffffff; + goto done; + } + + // check if there are more pages + last_page_loc = stb_vorbis_get_file_offset(f); + + // stop when the last_page flag is set, not when we reach eof; + // this allows us to stop short of a 'file_section' end without + // explicitly checking the length of the section + while (!last) { + set_file_offset(f, end); + if (!vorbis_find_page(f, &end, &last)) { + // the last page we found didn't have the 'last page' flag + // set. whoops! + break; + } + previous_safe = last_page_loc+1; + last_page_loc = stb_vorbis_get_file_offset(f); + } + + set_file_offset(f, last_page_loc); + + // parse the header + getn(f, (unsigned char *)header, 6); + // extract the absolute granule position + lo = get32(f); + hi = get32(f); + if (lo == 0xffffffff && hi == 0xffffffff) { + f->error = VORBIS_cant_find_last_page; + f->total_samples = SAMPLE_unknown; + goto done; + } + if (hi) + lo = 0xfffffffe; // saturate + f->total_samples = lo; + + f->p_last.page_start = last_page_loc; + f->p_last.page_end = end; + f->p_last.last_decoded_sample = lo; + + done: + set_file_offset(f, restore_offset); + } + return f->total_samples == SAMPLE_unknown ? 0 : f->total_samples; +} + +float stb_vorbis_stream_length_in_seconds(stb_vorbis *f) +{ + return stb_vorbis_stream_length_in_samples(f) / (float) f->sample_rate; +} + + + +int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output) +{ + int len, right,left,i; + if (IS_PUSH_MODE(f)) return error(f, VORBIS_invalid_api_mixing); + + if (!vorbis_decode_packet(f, &len, &left, &right)) { + f->channel_buffer_start = f->channel_buffer_end = 0; + return 0; + } + + len = vorbis_finish_frame(f, len, left, right); + for (i=0; i < f->channels; ++i) + f->outputs[i] = f->channel_buffers[i] + left; + + f->channel_buffer_start = left; + f->channel_buffer_end = left+len; + + if (channels) *channels = f->channels; + if (output) *output = f->outputs; + return len; +} + +#ifndef STB_VORBIS_NO_STDIO + +stb_vorbis * stb_vorbis_open_file_section(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.f = file; + p.f_start = (uint32) ftell(file); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + *f = p; + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_file(FILE *file, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + unsigned int len, start; + start = (unsigned int) ftell(file); + fseek(file, 0, SEEK_END); + len = (unsigned int) (ftell(file) - start); + fseek(file, start, SEEK_SET); + return stb_vorbis_open_file_section(file, close_on_free, error, alloc, len); +} + +stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc) +{ + FILE *f = fopen(filename, "rb"); + if (f) + return stb_vorbis_open_file(f, TRUE, error, alloc); + if (error) *error = VORBIS_file_open_failure; + return NULL; +} +#endif // STB_VORBIS_NO_STDIO + +#ifdef __SDL_SOUND_INTERNAL__ +stb_vorbis * stb_vorbis_open_rwops_section(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc, unsigned int length) +{ + stb_vorbis *f, p; + vorbis_init(&p, alloc); + p.rwops = rwops; + p.rwops_start = (uint32) SDL_RWtell(rwops); + p.stream_len = length; + p.close_on_free = close_on_free; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + memcpy(f, &p, sizeof (stb_vorbis)); + vorbis_pump_first_frame(f); + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +stb_vorbis * stb_vorbis_open_rwops(SDL_RWops *rwops, int close_on_free, int *error, const stb_vorbis_alloc *alloc) +{ + const unsigned int start = (unsigned int) SDL_RWtell(rwops); + const unsigned int len = (unsigned int) (SDL_RWsize(rwops) - start); + return stb_vorbis_open_rwops_section(rwops, close_on_free, error, alloc, len); +} +#endif + +stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) +{ + stb_vorbis *f, p; + if (data == NULL) return NULL; + vorbis_init(&p, alloc); + p.stream = (uint8 *) data; + p.stream_end = (uint8 *) data + len; + p.stream_start = (uint8 *) p.stream; + p.stream_len = len; + p.push_mode = FALSE; + if (start_decoder(&p)) { + f = vorbis_alloc(&p); + if (f) { + memcpy(f, &p, sizeof (stb_vorbis)); + vorbis_pump_first_frame(f); + if (error) *error = VORBIS__no_error; + return f; + } + } + if (error) *error = p.error; + vorbis_deinit(&p); + return NULL; +} + +#ifndef STB_VORBIS_NO_INTEGER_CONVERSION +#define PLAYBACK_MONO 1 +#define PLAYBACK_LEFT 2 +#define PLAYBACK_RIGHT 4 + +#define L (PLAYBACK_LEFT | PLAYBACK_MONO) +#define C (PLAYBACK_LEFT | PLAYBACK_RIGHT | PLAYBACK_MONO) +#define R (PLAYBACK_RIGHT | PLAYBACK_MONO) + +static int8 channel_position[7][6] = +{ + { 0 }, + { C }, + { L, R }, + { L, C, R }, + { L, R, L, R }, + { L, C, R, L, R }, + { L, C, R, L, R, C }, +}; + + +#ifndef STB_VORBIS_NO_FAST_SCALED_FLOAT + typedef union { + float f; + int i; + } float_conv; + typedef char stb_vorbis_float_size_test[sizeof(float)==4 && sizeof(int) == 4]; + #define FASTDEF(x) float_conv x + // add (1<<23) to convert to int, then divide by 2^SHIFT, then add 0.5/2^SHIFT to round + #define MAGIC(SHIFT) (1.5f * (1 << (23-SHIFT)) + 0.5f/(1 << SHIFT)) + #define ADDEND(SHIFT) (((150-SHIFT) << 23) + (1 << 22)) + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) (temp.f = (x) + MAGIC(s), temp.i - ADDEND(s)) + #define check_endianness() +#else + #define FAST_SCALED_FLOAT_TO_INT(temp,x,s) ((int) ((x) * (1 << (s)))) + #define check_endianness() + #define FASTDEF(x) +#endif + +static void copy_samples(short *dest, float *src, int len) +{ + int i; + check_endianness(); + for (i=0; i < len; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp, src[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + dest[i] = v; + } +} + +static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE; + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE) { + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + if (channel_position[num_c][j] & mask) { + for (i=0; i < n; ++i) + buffer[i] += data[j][d_offset+o+i]; + } + } + for (i=0; i < n; ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o+i] = v; + } + } +} + +static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) +{ + #define BUFFER_SIZE 32 + float buffer[BUFFER_SIZE]; + int i,j,o,n = BUFFER_SIZE >> 1; + // o is the offset in the source data + check_endianness(); + for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + // o2 is the offset in the output data + int o2 = o << 1; + memset(buffer, 0, sizeof(buffer)); + if (o + n > len) n = len - o; + for (j=0; j < num_c; ++j) { + int m = channel_position[num_c][j] & (PLAYBACK_LEFT | PLAYBACK_RIGHT); + if (m == (PLAYBACK_LEFT | PLAYBACK_RIGHT)) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_LEFT) { + for (i=0; i < n; ++i) { + buffer[i*2+0] += data[j][d_offset+o+i]; + } + } else if (m == PLAYBACK_RIGHT) { + for (i=0; i < n; ++i) { + buffer[i*2+1] += data[j][d_offset+o+i]; + } + } + } + for (i=0; i < (n<<1); ++i) { + FASTDEF(temp); + int v = FAST_SCALED_FLOAT_TO_INT(temp,buffer[i],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + output[o2+i] = v; + } + } +} + +static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) +{ + int i; + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + static int channel_selector[3][2] = { {0}, {PLAYBACK_MONO}, {PLAYBACK_LEFT, PLAYBACK_RIGHT} }; + for (i=0; i < buf_c; ++i) + compute_samples(channel_selector[buf_c][i], buffer[i]+b_offset, data_c, data, d_offset, samples); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + for (i=0; i < limit; ++i) + copy_samples(buffer[i]+b_offset, data[i]+d_offset, samples); + for ( ; i < buf_c; ++i) + memset(buffer[i]+b_offset, 0, sizeof(short) * samples); + } +} + +int stb_vorbis_get_frame_short(stb_vorbis *f, int num_c, short **buffer, int num_samples) +{ + float **output; + int len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len > num_samples) len = num_samples; + if (len) + convert_samples_short(num_c, buffer, 0, f->channels, output, 0, len); + return len; +} + +static void convert_channels_short_interleaved(int buf_c, short *buffer, int data_c, float **data, int d_offset, int len) +{ + int i; + check_endianness(); + if (buf_c != data_c && buf_c <= 2 && data_c <= 6) { + assert(buf_c == 2); + for (i=0; i < buf_c; ++i) + compute_stereo_samples(buffer, data_c, data, d_offset, len); + } else { + int limit = buf_c < data_c ? buf_c : data_c; + int j; + for (j=0; j < len; ++j) { + for (i=0; i < limit; ++i) { + FASTDEF(temp); + float f = data[i][d_offset+j]; + int v = FAST_SCALED_FLOAT_TO_INT(temp, f,15);//data[i][d_offset+j],15); + if ((unsigned int) (v + 32768) > 65535) + v = v < 0 ? -32768 : 32767; + *buffer++ = v; + } + for ( ; i < buf_c; ++i) + *buffer++ = 0; + } + } +} + +int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts) +{ + float **output; + int len; + if (num_c == 1) return stb_vorbis_get_frame_short(f,num_c,&buffer, num_shorts); + len = stb_vorbis_get_frame_float(f, NULL, &output); + if (len) { + if (len*num_c > num_shorts) len = num_shorts / num_c; + convert_channels_short_interleaved(num_c, buffer, f->channels, output, 0, len); + } + return len; +} + +int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts) +{ + float **outputs; + int len = num_shorts / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_channels_short_interleaved(channels, buffer, f->channels, f->channel_buffers, f->channel_buffer_start, k); + buffer += k*channels; + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int len) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + if (k) + convert_samples_short(channels, buffer, n, f->channels, f->channel_buffers, f->channel_buffer_start, k); + n += k; + f->channel_buffer_start += k; + if (n == len) break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) break; + } + return n; +} + +#ifndef STB_VORBIS_NO_STDIO +int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // NO_STDIO + +int stb_vorbis_decode_memory(const uint8 *mem, int len, int *channels, int *sample_rate, short **output) +{ + int data_len, offset, total, limit, error; + short *data; + stb_vorbis *v = stb_vorbis_open_memory(mem, len, &error, NULL); + if (v == NULL) return -1; + limit = v->channels * 4096; + *channels = v->channels; + if (sample_rate) + *sample_rate = v->sample_rate; + offset = data_len = 0; + total = limit; + data = (short *) malloc(total * sizeof(*data)); + if (data == NULL) { + stb_vorbis_close(v); + return -2; + } + for (;;) { + int n = stb_vorbis_get_frame_short_interleaved(v, v->channels, data+offset, total-offset); + if (n == 0) break; + data_len += n; + offset += n * v->channels; + if (offset + limit > total) { + short *data2; + total *= 2; + data2 = (short *) realloc(data, total * sizeof(*data)); + if (data2 == NULL) { + free(data); + stb_vorbis_close(v); + return -2; + } + data = data2; + } + } + *output = data; + stb_vorbis_close(v); + return data_len; +} +#endif // STB_VORBIS_NO_INTEGER_CONVERSION + +int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats) +{ + float **outputs; + int len = num_floats / channels; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < len) { + int i,j; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= len) k = len - n; + for (j=0; j < k; ++j) { + for (i=0; i < z; ++i) + *buffer++ = f->channel_buffers[i][f->channel_buffer_start+j]; + for ( ; i < channels; ++i) + *buffer++ = 0; + } + n += k; + f->channel_buffer_start += k; + if (n == len) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} + +int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples) +{ + float **outputs; + int n=0; + int z = f->channels; + if (z > channels) z = channels; + while (n < num_samples) { + int i; + int k = f->channel_buffer_end - f->channel_buffer_start; + if (n+k >= num_samples) k = num_samples - n; + if (k) { + for (i=0; i < z; ++i) + memcpy(buffer[i]+n, f->channel_buffers[i]+f->channel_buffer_start, sizeof(float)*k); + for ( ; i < channels; ++i) + memset(buffer[i]+n, 0, sizeof(float) * k); + } + n += k; + f->channel_buffer_start += k; + if (n == num_samples) + break; + if (!stb_vorbis_get_frame_float(f, NULL, &outputs)) + break; + } + return n; +} +#endif // STB_VORBIS_NO_PULLDATA_API + +/* Version history + 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files + 1.11 - 2017-07-23 - fix MinGW compilation + 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory + 1.09 - 2016-04-04 - back out 'avoid discarding last frame' fix from previous version + 1.08 - 2016-04-02 - fixed multiple warnings; fix setup memory leaks; + avoid discarding last frame of audio data + 1.07 - 2015-01-16 - fixed some warnings, fix mingw, const-correct API + some more crash fixes when out of memory or with corrupt files + 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson) + some crash fixes when out of memory or with corrupt files + 1.05 - 2015-04-19 - don't define __forceinline if it's redundant + 1.04 - 2014-08-27 - fix missing const-correct case in API + 1.03 - 2014-08-07 - Warning fixes + 1.02 - 2014-07-09 - Declare qsort compare function _cdecl on windows + 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float + 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in multichannel + (API change) report sample rate for decode-full-file funcs + 0.99996 - bracket #include for macintosh compilation by Laurent Gomila + 0.99995 - use union instead of pointer-cast for fast-float-to-int to avoid alias-optimization problem + 0.99994 - change fast-float-to-int to work in single-precision FPU mode, remove endian-dependence + 0.99993 - remove assert that fired on legal files with empty tables + 0.99992 - rewind-to-start + 0.99991 - bugfix to stb_vorbis_get_samples_short by Bernhard Wodo + 0.9999 - (should have been 0.99990) fix no-CRT support, compiling as C++ + 0.9998 - add a full-decode function with a memory source + 0.9997 - fix a bug in the read-from-FILE case in 0.9996 addition + 0.9996 - query length of vorbis stream in samples/seconds + 0.9995 - bugfix to another optimization that only happened in certain files + 0.9994 - bugfix to one of the optimizations that caused significant (but inaudible?) errors + 0.9993 - performance improvements; runs in 99% to 104% of time of reference implementation + 0.9992 - performance improvement of IMDCT; now performs close to reference implementation + 0.9991 - performance improvement of IMDCT + 0.999 - (should have been 0.9990) performance improvement of IMDCT + 0.998 - no-CRT support from Casey Muratori + 0.997 - bugfixes for bugs found by Terje Mathisen + 0.996 - bugfix: fast-huffman decode initialized incorrectly for sparse codebooks; fixing gives 10% speedup - found by Terje Mathisen + 0.995 - bugfix: fix to 'effective' overrun detection - found by Terje Mathisen + 0.994 - bugfix: garbage decode on final VQ symbol of a non-multiple - found by Terje Mathisen + 0.993 - bugfix: pushdata API required 1 extra byte for empty page (failed to consume final page if empty) - found by Terje Mathisen + 0.992 - fixes for MinGW warning + 0.991 - turn fast-float-conversion on by default + 0.990 - fix push-mode seek recovery if you seek into the headers + 0.98b - fix to bad release of 0.98 + 0.98 - fix push-mode seek recovery; robustify float-to-int and support non-fast mode + 0.97 - builds under c++ (typecasting, don't use 'class' keyword) + 0.96 - somehow MY 0.95 was right, but the web one was wrong, so here's my 0.95 rereleased as 0.96, fixes a typo in the clamping code + 0.95 - clamping code for 16-bit functions + 0.94 - not publically released + 0.93 - fixed all-zero-floor case (was decoding garbage) + 0.92 - fixed a memory leak + 0.91 - conditional compiles to omit parts of the API and the infrastructure to support them: STB_VORBIS_NO_PULLDATA_API, STB_VORBIS_NO_PUSHDATA_API, STB_VORBIS_NO_STDIO, STB_VORBIS_NO_INTEGER_CONVERSION + 0.90 - first public release +*/ + +#endif // STB_VORBIS_HEADER_ONLY + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/binding/binding-mri.cpp b/binding/binding-mri.cpp index bd6dbf7f..00eee22b 100644 --- a/binding/binding-mri.cpp +++ b/binding/binding-mri.cpp @@ -64,7 +64,11 @@ extern const char module_rpg3[]; // Scripts to run at some point during Ruby execution #ifdef EASY_POKE +#ifndef MKXPZ_BUILD_XCODE #include "EssentialsCompatibility.rb.xxd" +#else +#include "CocoaHelpers.hpp" +#endif #endif static void mriBindingExecute(); @@ -625,8 +629,15 @@ static void runRMXPScripts(BacktraceData &btData) { // make sure to check whether it's the last one or not and run // any extra stuff before the end (primarily stupid Essentials fixes) // Will be placed within a build option later if I decide to add more +#ifndef MKXPZ_BUILD_XCODE #define SCRIPT(name) rb_str_new((const char*)&___scripts_##name##_rb, ___scripts_##name##_rb_len), #name " (Internal)" #define EVALFILE(name) if (!evalScript(SCRIPT(name))) break; +#else + #define EVALFILE(name) { \ + std::string s = Cocoa::getFile("BindingScripts/" #name, "rb"); \ + if (!evalScript(rb_str_new_cstr(s.c_str()), #name)) break; \ + } +#endif if (i + 2 == scriptCount){ #ifdef EASY_POKE if (minimonsters > 0 && !RTEST(rb_gv_get("Z_NOPOKEFIX"))){ diff --git a/binding/cusl-binding.cpp b/binding/cusl-binding.cpp index 60fef739..e14045bd 100644 --- a/binding/cusl-binding.cpp +++ b/binding/cusl-binding.cpp @@ -1,3 +1,4 @@ +#ifdef HAVE_STEAMSHIM #include "binding-util.h" #include "steamshim_child.h" @@ -217,3 +218,4 @@ void CUSLBindingInit() { _rb_define_module_function(mSteamLite, "reset_all_stats", CUSLResetAllStats); } +#endif diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 00000000..47e2eeb0 --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,5 @@ +DerivedData/ +*Dependencies/ +*Dependencies.dmg +gls* +*.dmg \ No newline at end of file diff --git a/macos/CocoaHelpers.hpp b/macos/CocoaHelpers.hpp new file mode 100644 index 00000000..80390e60 --- /dev/null +++ b/macos/CocoaHelpers.hpp @@ -0,0 +1,20 @@ +// +// CocoaHelpers.hpp +// PlayerLegacy +// +// Created by ゾロアーク on 11/18/20. +// + +#ifndef CocoaHelpers_hpp +#define CocoaHelpers_hpp + +#include +#include + +class Cocoa { +public: + static std::string getFile(const char *baseName, const char *ext); + static std::string getFilePath(const char *baseName, const char *ext); +}; + +#endif /* CocoaHelpers_hpp */ diff --git a/macos/CocoaHelpers.mm b/macos/CocoaHelpers.mm new file mode 100644 index 00000000..72adc93b --- /dev/null +++ b/macos/CocoaHelpers.mm @@ -0,0 +1,34 @@ +// +// CocoaHelpers.cpp +// PlayerLegacy +// +// Created by ゾロアーク on 11/18/20. +// + +#import "CocoaHelpers.hpp" +#import +#import + +// ObjFW is a bit of a dick to strings when Cocoa gets involved +// Can't use literals when using both Apple and ObjFW headers, meh + +// This is a pretty lazy header, but it'll do for the moment while +// I work on Apple stuff, lots to do +#define NSSTR(ptr) [NSString stringWithUTF8String: ptr] + +std::string Cocoa::getFile(const char* baseName, const char* ext) { + + return std::string([[NSString stringWithContentsOfFile: NSSTR(getFilePath(baseName, ext).c_str())] UTF8String]); +} + +std::string Cocoa::getFilePath(const char *baseName, const char *ext) { + NSBundle *assetBundle = [NSBundle bundleWithPath: + [NSString stringWithFormat: + NSSTR("%@/%s"), + NSBundle.mainBundle.resourcePath, + "Assets.bundle" + ] + ]; + + return std::string([assetBundle pathForResource: NSSTR(baseName) ofType: NSSTR(ext)].UTF8String); +} diff --git a/macos/Config.xcconfig b/macos/Config.xcconfig new file mode 100644 index 00000000..6ac5fc91 --- /dev/null +++ b/macos/Config.xcconfig @@ -0,0 +1,55 @@ +// +// Config.xcconfig +// PlayerLegacy +// +// Created by ゾロアーク on 11/17/20. +// + +// Uncomment this for the Essentials crap +// GCC_PREPROCESSOR_DEFINITIONS = $(inherited) EASY_POKE + +// The path to your build dependencies, or the included (and mounted) .dmg +DEPENDENCY_SEARCH_PATH = $(PROJECT_DIR)/MKXPZ-Dependencies/mac + +// The version of Ruby to use +// It's really only important for the disk image, +// if you install Ruby yourself just change paths in the project on your own +RUBY_INSTALL_PREFIX = $(DEPENDENCY_SEARCH_PATH)/prefix/opt/ruby$(MRI_VERSION) + +// Default, don't change this here, change it in the target settings +MRI_VERSION=2.7.0 + +INFOPLIST_FILE = $(PROJECT_DIR)/Info.plist +CODE_SIGN_ENTITLEMENTS = $(PROJECT_DIR)/entitlements.plist +OTHER_CFLAGS = -fconstant-string-class=OFConstantString -fno-constant-cfstrings $(inherited) +CLANG_ENABLE_MODULES = NO +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) MKXPZ_BUILD_XCODE GLES2_HEADER ALCDEVICE_STRUCT=ALCdevice_struct SHARED_FLUID + +GCC_C_LANGUAGE_STANDARD = gnu11 +GCC_CXX_LANGUAGE_STANDARD = c++11 + +LIBRARY_SEARCH_PATHS = "$(RUBY_INSTALL_PREFIX)/lib" "$(DEPENDENCY_SEARCH_PATH)/prefix/lib" "$(DEPENDENCY_SEARCH_PATH)/prefix/lib64" +HEADER_SEARCH_PATHS = "$(PROJECT_DIR)/../**" "$(DEPENDENCY_SEARCH_PATH)/prefix/include" "$(DEPENDENCY_SEARCH_PATH)/prefix/lib/**" + + +// Project includes +EXCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = $(inherited) MKXPZ-Dependencies ../build + +// sigc++ +HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/prefix/include/sigc++-2.0" + +// pixman +HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/prefix/include/pixman-1" + +// Usually you need to access Framework headers like "SDL2/SDL.h" +// MKXP's source isn't written with macOS in mind though, +// so gotta search the framework headers +HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/SDL2.framework/Headers/**" +HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/SDL2_ttf.framework/Headers/**" +HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/SDL2_image.framework/Headers/**" +HEADER_SEARCH_PATHS = $(inherited) "$(DEPENDENCY_SEARCH_PATH)/frameworks/MetalANGLE.framework/Headers/**" + +FRAMEWORK_SEARCH_PATHS = "$(DEPENDENCY_SEARCH_PATH)/frameworks" $(inherited) + +// Configuration settings file format documentation can be found at: +// https://help.apple.com/xcode/#/dev745c5c974 diff --git a/macos/Deps.make b/macos/Deps.make new file mode 100644 index 00000000..db79f38f --- /dev/null +++ b/macos/Deps.make @@ -0,0 +1,23 @@ +GOODLS_URL := https://github.com/tanaikech/goodls/releases/download/v1.2.7/goodls_darwin_amd64 +DMG_URL := https://drive.google.com/file/d/1-9Pt5bxvFUuO_yPFzC_h3WvQ9LDA6Q5K/view?usp=sharing +DMG_NAME := Dependencies +GOODLS := gls + +DEP_FOLDER := MKXPZ-Dependencies + +deps: $(DMG_NAME).dmg + hdiutil attach "$(DMG_NAME).dmg" -mountroot . -quiet + +$(DMG_NAME).dmg: $(GOODLS) + "./$(GOODLS)" -u "$(DMG_URL)" -f "$(DMG_NAME).dmg" + +$(GOODLS): + curl -L "$(GOODLS_URL)" > "$(GOODLS)" + chmod +x "$(GOODLS)" + +unmount: + -hdiutil detach $(DEP_FOLDER) -quiet + +clean: unmount + rm -rf "$(GOODLS)" "$(DMG_NAME).dmg" + diff --git a/macos/Info.plist b/macos/Info.plist index 455cc982..992dd474 100644 --- a/macos/Info.plist +++ b/macos/Info.plist @@ -2,26 +2,28 @@ - CFBundleGetInfoString - mkxp-z CFBundleExecutable mkxp-z - CFBundleIdentifier - com.zoro.mkxpz - CFBundleName + CFBundleGetInfoString mkxp-z CFBundleIconFile icon.icns - CFBundleShortVersionString - 1.3.0 + CFBundleIdentifier + com.zoro.mkxpz CFBundleInfoDictionaryVersion 6.0 + CFBundleName + mkxp-z CFBundlePackageType APPL + CFBundleShortVersionString + 1.3.0 IFMajorVersion 0 IFMinorVersion 1 + LSApplicationCategoryType + public.app-category.role-playing-games NSRequiresAquaSystemAppearance False SDL_FILESYSTEM_BASE_DIR_TYPE diff --git a/macos/Makefile b/macos/Makefile new file mode 100644 index 00000000..ce65b069 --- /dev/null +++ b/macos/Makefile @@ -0,0 +1,15 @@ +CONFIG := Config.xcconfig +XCARGS := -xcconfig $(CONFIG) -scheme Player + +debug: deps + xcodebuild -configuration Debug $(XCARGS) + +release: deps + xcodebuild -configuration Release $(XCARGS) + +deps: + @make -f Deps.make + +clean: + @make -f Deps.make clean + rm -rf "DerivedData" \ No newline at end of file diff --git a/macos/entitlements.plist b/macos/entitlements.plist index 2b052af1..411022eb 100644 --- a/macos/entitlements.plist +++ b/macos/entitlements.plist @@ -1,26 +1,14 @@ - + + - - - - com.apple.security.app-sandbox - - - com.apple.security.network.client - - com.apple.security.network.server - - com.apple.security.files.user-selected.read-write - - com.apple.security.files.downloads.read-write - - com.apple.security.assets.pictures.read-write - - com.apple.security.cs.disable-library-validation - - com.apple.security.cs.allow-unsigned-executable-memory - - com.apple.security.cs.allow-jit - - - \ No newline at end of file + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + + diff --git a/macos/lib/.gitignore b/macos/lib/.gitignore deleted file mode 100644 index 1da08e07..00000000 --- a/macos/lib/.gitignore +++ /dev/null @@ -1 +0,0 @@ -MetalANGLE.framework/* \ No newline at end of file diff --git a/macos/lib/MetalANGLE.framework.zip b/macos/lib/MetalANGLE.framework.zip deleted file mode 100644 index 1dd6b6f3..00000000 Binary files a/macos/lib/MetalANGLE.framework.zip and /dev/null differ diff --git a/macos/macpack.sh b/macos/macpack.sh index 608ae7a2..d4133fba 100755 --- a/macos/macpack.sh +++ b/macos/macpack.sh @@ -2,16 +2,8 @@ EXE=${MESON_INSTALL_PREFIX}/Contents/MacOS/$2 -ANGLE="${MESON_SOURCE_ROOT}/macos/lib/MetalANGLE.framework" FRAMEWORKS="${MESON_INSTALL_PREFIX}/Contents/Frameworks" -if [ -n "$(otool -L $EXE | grep ANGLE)" ] && [ -d $ANGLE ]; then - if [ ! -d $FRAMEWORKS ]; then - mkdir -p $FRAMEWORKS - fi - cp -a $ANGLE $FRAMEWORKS -fi - if [ -n "$1" ]; then echo "Setting up steam_api manually..." if [ ! -d $FRAMEWORKS ]; then diff --git a/macos/misc/Assets.plist b/macos/misc/Assets.plist new file mode 100644 index 00000000..fb1c898b --- /dev/null +++ b/macos/misc/Assets.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSPrincipalClass + + + diff --git a/macos/mkxp-z.xcodeproj/project.pbxproj b/macos/mkxp-z.xcodeproj/project.pbxproj new file mode 100644 index 00000000..cfd6c36e --- /dev/null +++ b/macos/mkxp-z.xcodeproj/project.pbxproj @@ -0,0 +1,2438 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 3BA08E5825661C4700449CFF /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5225661C4600449CFF /* Vorbis.framework */; }; + 3BA08E5925661C4700449CFF /* Vorbis.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5225661C4600449CFF /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E5A25661C4700449CFF /* SDL2_ttf.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5325661C4700449CFF /* SDL2_ttf.framework */; }; + 3BA08E5B25661C4700449CFF /* SDL2_ttf.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5325661C4700449CFF /* SDL2_ttf.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E5C25661C4700449CFF /* MetalANGLE.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5425661C4700449CFF /* MetalANGLE.framework */; }; + 3BA08E5D25661C4800449CFF /* MetalANGLE.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5425661C4700449CFF /* MetalANGLE.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E5E25661C4800449CFF /* Ogg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5525661C4700449CFF /* Ogg.framework */; }; + 3BA08E5F25661C4800449CFF /* Ogg.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5525661C4700449CFF /* Ogg.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E6025661C4800449CFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5625661C4700449CFF /* SDL2.framework */; }; + 3BA08E6125661C4800449CFF /* SDL2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5625661C4700449CFF /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E6225661C4800449CFF /* SDL2_image.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5725661C4700449CFF /* SDL2_image.framework */; }; + 3BA08E6325661C4800449CFF /* SDL2_image.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5725661C4700449CFF /* SDL2_image.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E6525661CA600449CFF /* libfluidsynth.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6425661CA600449CFF /* libfluidsynth.3.dylib */; }; + 3BA08E6625661CA600449CFF /* libfluidsynth.3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6425661CA600449CFF /* libfluidsynth.3.dylib */; }; + 3BA08E6725661CAA00449CFF /* libfluidsynth.3.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6425661CA600449CFF /* libfluidsynth.3.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08E7025661CDA00449CFF /* libphysfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6925661CD900449CFF /* libphysfs.a */; }; + 3BA08E7125661CDA00449CFF /* libphysfs.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6925661CD900449CFF /* libphysfs.a */; }; + 3BA08E7225661CDA00449CFF /* libobjfwbridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6A25661CD900449CFF /* libobjfwbridge.a */; }; + 3BA08E7325661CDA00449CFF /* libobjfwbridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6A25661CD900449CFF /* libobjfwbridge.a */; }; + 3BA08E7425661CDA00449CFF /* libobjfw.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6B25661CD900449CFF /* libobjfw.a */; }; + 3BA08E7525661CDA00449CFF /* libobjfw.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6B25661CD900449CFF /* libobjfw.a */; }; + 3BA08E7625661CDA00449CFF /* libsigc-2.0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6C25661CD900449CFF /* libsigc-2.0.a */; }; + 3BA08E7725661CDA00449CFF /* libsigc-2.0.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6C25661CD900449CFF /* libsigc-2.0.a */; }; + 3BA08E7825661CDA00449CFF /* libpixman-1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6D25661CD900449CFF /* libpixman-1.a */; }; + 3BA08E7925661CDA00449CFF /* libpixman-1.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6D25661CD900449CFF /* libpixman-1.a */; }; + 3BA08E7C25661D2C00449CFF /* libruby.1.8.7.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E7B25661D2C00449CFF /* libruby.1.8.7.dylib */; }; + 3BA08E7E25661D3700449CFF /* libruby.2.7.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E7D25661D3700449CFF /* libruby.2.7.dylib */; }; + 3BA08E7F25661D5600449CFF /* libruby.2.7.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E7D25661D3700449CFF /* libruby.2.7.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08E8025661E3400449CFF /* MetalANGLE.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5425661C4700449CFF /* MetalANGLE.framework */; }; + 3BA08E8125661E3400449CFF /* MetalANGLE.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5425661C4700449CFF /* MetalANGLE.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E8225661E3400449CFF /* Ogg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5525661C4700449CFF /* Ogg.framework */; }; + 3BA08E8325661E3400449CFF /* Ogg.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5525661C4700449CFF /* Ogg.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E8425661E3400449CFF /* SDL2_image.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5725661C4700449CFF /* SDL2_image.framework */; }; + 3BA08E8525661E3500449CFF /* SDL2_image.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5725661C4700449CFF /* SDL2_image.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E8625661E3500449CFF /* SDL2_ttf.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5325661C4700449CFF /* SDL2_ttf.framework */; }; + 3BA08E8725661E3500449CFF /* SDL2_ttf.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5325661C4700449CFF /* SDL2_ttf.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E8825661E3500449CFF /* SDL2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5625661C4700449CFF /* SDL2.framework */; }; + 3BA08E8925661E3500449CFF /* SDL2.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5625661C4700449CFF /* SDL2.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E8A25661E3500449CFF /* Vorbis.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5225661C4600449CFF /* Vorbis.framework */; }; + 3BA08E8B25661E3500449CFF /* Vorbis.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E5225661C4600449CFF /* Vorbis.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 3BA08E8C25661E4100449CFF /* libruby.1.8.7.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E7B25661D2C00449CFF /* libruby.1.8.7.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08E8D25661E4300449CFF /* libfluidsynth.3.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3BA08E6425661CA600449CFF /* libfluidsynth.3.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08E9A256638C600449CFF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46925651C1B003DAD8A /* AudioToolbox.framework */; }; + 3BA08E9B256638C900449CFF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46925651C1B003DAD8A /* AudioToolbox.framework */; }; + 3BA08EAD256642A300449CFF /* bitmapBlit.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4BB25654AD7003DAD8A /* bitmapBlit.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EAE256642A300449CFF /* blur.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4AC25654AD6003DAD8A /* blur.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EAF256642A300449CFF /* blurH.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B825654AD7003DAD8A /* blurH.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB0256642A300449CFF /* blurV.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B025654AD6003DAD8A /* blurV.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB1256642A300449CFF /* common.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4BC25654AD7003DAD8A /* common.h */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB2256642A300449CFF /* flashMap.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4C425654AD8003DAD8A /* flashMap.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB3256642A300449CFF /* flatColor.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B725654AD7003DAD8A /* flatColor.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB4256642A300449CFF /* gray.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4AE25654AD6003DAD8A /* gray.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB5256642A300449CFF /* hue.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4C325654AD8003DAD8A /* hue.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB6256642A300449CFF /* minimal.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B325654AD6003DAD8A /* minimal.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB7256642A300449CFF /* plane.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B625654AD6003DAD8A /* plane.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB8256642A300449CFF /* simple.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4AF25654AD6003DAD8A /* simple.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EB9256642A300449CFF /* simple.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B525654AD6003DAD8A /* simple.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EBA256642A300449CFF /* simpleAlpha.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4BD25654AD7003DAD8A /* simpleAlpha.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EBB256642A300449CFF /* simpleAlphaUni.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B925654AD7003DAD8A /* simpleAlphaUni.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EBC256642A300449CFF /* simpleColor.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4BF25654AD8003DAD8A /* simpleColor.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EBD256642A300449CFF /* simpleColor.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4BE25654AD8003DAD8A /* simpleColor.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EBE256642A300449CFF /* simpleMatrix.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4AD25654AD6003DAD8A /* simpleMatrix.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EBF256642A300449CFF /* sprite.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4C225654AD8003DAD8A /* sprite.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EC0256642A300449CFF /* sprite.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B125654AD6003DAD8A /* sprite.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EC1256642A300449CFF /* tilemap.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4C125654AD8003DAD8A /* tilemap.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EC2256642A300449CFF /* tilemap.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B225654AD6003DAD8A /* tilemap.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EC3256642A300449CFF /* tilemapvx.vert in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4C025654AD8003DAD8A /* tilemapvx.vert */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EC4256642A300449CFF /* trans.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4B425654AD6003DAD8A /* trans.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EC5256642A300449CFF /* transSimple.frag in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4BA25654AD7003DAD8A /* transSimple.frag */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EC92566432900449CFF /* wqymicrohei.ttf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4F125654DE3003DAD8A /* wqymicrohei.ttf */; }; + 3BA08ECA2566432900449CFF /* liberation.ttf in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4F025654DDB003DAD8A /* liberation.ttf */; }; + 3BA08ECE2566437500449CFF /* icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 3BD2B4EE25654DC7003DAD8A /* icon.png */; }; + 3BA08ED4256643C200449CFF /* Assets.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3BA08EA4256641ED00449CFF /* Assets.bundle */; }; + 3BA08ED5256643E300449CFF /* Assets.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 3BA08EA4256641ED00449CFF /* Assets.bundle */; }; + 3BA08EE22566499C00449CFF /* EssentialsCompatibility.rb in CopyFiles */ = {isa = PBXBuildFile; fileRef = 3BD2B4C625654B6F003DAD8A /* EssentialsCompatibility.rb */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 3BA08EF925664BCC00449CFF /* SDL_sound_shn.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EE725664BCC00449CFF /* SDL_sound_shn.c */; }; + 3BA08EFA25664BCC00449CFF /* SDL_sound_shn.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EE725664BCC00449CFF /* SDL_sound_shn.c */; }; + 3BA08EFB25664BCC00449CFF /* SDL_sound_coreaudio.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EE925664BCC00449CFF /* SDL_sound_coreaudio.c */; }; + 3BA08EFC25664BCC00449CFF /* SDL_sound_coreaudio.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EE925664BCC00449CFF /* SDL_sound_coreaudio.c */; }; + 3BA08EFF25664BCC00449CFF /* SDL_sound_raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EEC25664BCC00449CFF /* SDL_sound_raw.c */; }; + 3BA08F0025664BCC00449CFF /* SDL_sound_raw.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EEC25664BCC00449CFF /* SDL_sound_raw.c */; }; + 3BA08F0125664BCC00449CFF /* SDL_sound_modplug.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EED25664BCC00449CFF /* SDL_sound_modplug.c */; }; + 3BA08F0225664BCC00449CFF /* SDL_sound_modplug.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EED25664BCC00449CFF /* SDL_sound_modplug.c */; }; + 3BA08F0325664BCC00449CFF /* SDL_sound_vorbis.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EEF25664BCC00449CFF /* SDL_sound_vorbis.c */; }; + 3BA08F0425664BCD00449CFF /* SDL_sound_vorbis.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EEF25664BCC00449CFF /* SDL_sound_vorbis.c */; }; + 3BA08F0525664BCD00449CFF /* SDL_sound.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF025664BCC00449CFF /* SDL_sound.c */; }; + 3BA08F0625664BCD00449CFF /* SDL_sound.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF025664BCC00449CFF /* SDL_sound.c */; }; + 3BA08F0725664BCD00449CFF /* SDL_sound_voc.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF125664BCC00449CFF /* SDL_sound_voc.c */; }; + 3BA08F0825664BCD00449CFF /* SDL_sound_voc.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF125664BCC00449CFF /* SDL_sound_voc.c */; }; + 3BA08F0925664BCD00449CFF /* SDL_sound_wav.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF325664BCC00449CFF /* SDL_sound_wav.c */; }; + 3BA08F0A25664BCD00449CFF /* SDL_sound_wav.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF325664BCC00449CFF /* SDL_sound_wav.c */; }; + 3BA08F0B25664BCD00449CFF /* SDL_sound_au.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF425664BCC00449CFF /* SDL_sound_au.c */; }; + 3BA08F0C25664BCD00449CFF /* SDL_sound_au.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF425664BCC00449CFF /* SDL_sound_au.c */; }; + 3BA08F0D25664BCD00449CFF /* SDL_sound_mp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF525664BCC00449CFF /* SDL_sound_mp3.c */; }; + 3BA08F0E25664BCD00449CFF /* SDL_sound_mp3.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF525664BCC00449CFF /* SDL_sound_mp3.c */; }; + 3BA08F0F25664BCD00449CFF /* SDL_sound_aiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF625664BCC00449CFF /* SDL_sound_aiff.c */; }; + 3BA08F1025664BCD00449CFF /* SDL_sound_aiff.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF625664BCC00449CFF /* SDL_sound_aiff.c */; }; + 3BA08F1125664BCD00449CFF /* SDL_sound_flac.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF725664BCC00449CFF /* SDL_sound_flac.c */; }; + 3BA08F1225664BCD00449CFF /* SDL_sound_flac.c in Sources */ = {isa = PBXBuildFile; fileRef = 3BA08EF725664BCC00449CFF /* SDL_sound_flac.c */; }; + 3BD2B3E125651943003DAD8A /* al-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23462564512A00C4A63D /* al-util.h */; }; + 3BD2B3E225651943003DAD8A /* aldatasource.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231C2564512500C4A63D /* aldatasource.h */; }; + 3BD2B3E325651943003DAD8A /* alstream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235E2564512C00C4A63D /* alstream.cpp */; }; + 3BD2B3E425651943003DAD8A /* alstream.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234B2564512B00C4A63D /* alstream.h */; }; + 3BD2B3E525651943003DAD8A /* audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23152564512400C4A63D /* audio.cpp */; }; + 3BD2B3E625651943003DAD8A /* audio.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235A2564512C00C4A63D /* audio.h */; }; + 3BD2B3E725651943003DAD8A /* audiostream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23432564512A00C4A63D /* audiostream.cpp */; }; + 3BD2B3E825651943003DAD8A /* audiostream.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23532564512B00C4A63D /* audiostream.h */; }; + 3BD2B3E925651943003DAD8A /* autotiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23412564512900C4A63D /* autotiles.cpp */; }; + 3BD2B3EA25651943003DAD8A /* autotilesvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23602564512D00C4A63D /* autotilesvx.cpp */; }; + 3BD2B3EB25651943003DAD8A /* binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233C2564512900C4A63D /* binding.h */; }; + 3BD2B3EC25651943003DAD8A /* bitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233F2564512900C4A63D /* bitmap.cpp */; }; + 3BD2B3ED25651943003DAD8A /* bitmap.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232A2564512700C4A63D /* bitmap.h */; }; + 3BD2B3EE25651943003DAD8A /* boost-hash.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236F2564512E00C4A63D /* boost-hash.h */; }; + 3BD2B3EF25651943003DAD8A /* config.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236D2564512E00C4A63D /* config.h */; }; + 3BD2B3F025651943003DAD8A /* config.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23662564512D00C4A63D /* config.mm */; }; + 3BD2B3F125651943003DAD8A /* debugwriter.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23282564512600C4A63D /* debugwriter.h */; }; + 3BD2B3F225651943003DAD8A /* disposable.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23102564512400C4A63D /* disposable.h */; }; + 3BD2B3F325651943003DAD8A /* etc-internal.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232B2564512700C4A63D /* etc-internal.h */; }; + 3BD2B3F425651943003DAD8A /* etc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236C2564512D00C4A63D /* etc.cpp */; }; + 3BD2B3F525651943003DAD8A /* etc.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235D2564512C00C4A63D /* etc.h */; }; + 3BD2B3F625651943003DAD8A /* eventthread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231D2564512500C4A63D /* eventthread.cpp */; }; + 3BD2B3F725651943003DAD8A /* eventthread.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23252564512600C4A63D /* eventthread.h */; }; + 3BD2B3F825651943003DAD8A /* exception.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23692564512D00C4A63D /* exception.h */; }; + 3BD2B3F925651943003DAD8A /* fake-api.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233A2564512900C4A63D /* fake-api.h */; }; + 3BD2B3FA25651943003DAD8A /* fake-api.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23322564512800C4A63D /* fake-api.mm */; }; + 3BD2B3FB25651943003DAD8A /* filesystem.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230C2564512300C4A63D /* filesystem.h */; }; + 3BD2B3FC25651943003DAD8A /* filesystem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231E2564512500C4A63D /* filesystem.mm */; }; + 3BD2B3FD25651943003DAD8A /* flashable.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23422564512A00C4A63D /* flashable.h */; }; + 3BD2B3FE25651943003DAD8A /* fluid-fun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234E2564512B00C4A63D /* fluid-fun.cpp */; }; + 3BD2B3FF25651943003DAD8A /* fluid-fun.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230F2564512400C4A63D /* fluid-fun.h */; }; + 3BD2B40025651943003DAD8A /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23262564512600C4A63D /* font.cpp */; }; + 3BD2B40125651943003DAD8A /* font.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23352564512800C4A63D /* font.h */; }; + 3BD2B40225651943003DAD8A /* gl-debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23672564512D00C4A63D /* gl-debug.cpp */; }; + 3BD2B40325651943003DAD8A /* gl-debug.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23132564512400C4A63D /* gl-debug.h */; }; + 3BD2B40425651943003DAD8A /* gl-fun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23402564512900C4A63D /* gl-fun.cpp */; }; + 3BD2B40525651943003DAD8A /* gl-fun.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231F2564512500C4A63D /* gl-fun.h */; }; + 3BD2B40625651943003DAD8A /* gl-meta.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23482564512B00C4A63D /* gl-meta.cpp */; }; + 3BD2B40725651943003DAD8A /* gl-meta.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23612564512D00C4A63D /* gl-meta.h */; }; + 3BD2B40825651943003DAD8A /* gl-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236A2564512D00C4A63D /* gl-util.h */; }; + 3BD2B40925651943003DAD8A /* global-ibo.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23632564512D00C4A63D /* global-ibo.h */; }; + 3BD2B40A25651943003DAD8A /* glstate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23712564512E00C4A63D /* glstate.cpp */; }; + 3BD2B40B25651943003DAD8A /* glstate.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23562564512C00C4A63D /* glstate.h */; }; + 3BD2B40C25651943003DAD8A /* graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231A2564512500C4A63D /* graphics.cpp */; }; + 3BD2B40D25651943003DAD8A /* graphics.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23192564512500C4A63D /* graphics.h */; }; + 3BD2B40E25651943003DAD8A /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23372564512800C4A63D /* input.cpp */; }; + 3BD2B40F25651943003DAD8A /* input.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23302564512700C4A63D /* input.h */; }; + 3BD2B41025651943003DAD8A /* intrulist.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235F2564512C00C4A63D /* intrulist.h */; }; + 3BD2B41125651943003DAD8A /* keybindings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232E2564512700C4A63D /* keybindings.cpp */; }; + 3BD2B41225651943003DAD8A /* keybindings.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23492564512B00C4A63D /* keybindings.h */; }; + 3BD2B41325651943003DAD8A /* lang-fun.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235C2564512C00C4A63D /* lang-fun.h */; }; + 3BD2B41425651943003DAD8A /* lang-fun.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23222564512600C4A63D /* lang-fun.mm */; }; + 3BD2B41525651943003DAD8A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23702564512E00C4A63D /* main.mm */; }; + 3BD2B41625651943003DAD8A /* midisource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23382564512800C4A63D /* midisource.cpp */; }; + 3BD2B41725651943003DAD8A /* plane.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232C2564512700C4A63D /* plane.cpp */; }; + 3BD2B41825651943003DAD8A /* plane.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23652564512D00C4A63D /* plane.h */; }; + 3BD2B41925651943003DAD8A /* quad.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23272564512600C4A63D /* quad.h */; }; + 3BD2B41A25651943003DAD8A /* quadarray.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230B2564512300C4A63D /* quadarray.h */; }; + 3BD2B41B25651943003DAD8A /* rgssad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234A2564512B00C4A63D /* rgssad.cpp */; }; + 3BD2B41C25651943003DAD8A /* rgssad.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23202564512500C4A63D /* rgssad.h */; }; + 3BD2B41D25651943003DAD8A /* scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23542564512B00C4A63D /* scene.cpp */; }; + 3BD2B41E25651943003DAD8A /* scene.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23342564512800C4A63D /* scene.h */; }; + 3BD2B41F25651943003DAD8A /* sdl-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231B2564512500C4A63D /* sdl-util.h */; }; + 3BD2B42025651943003DAD8A /* sdlsoundsource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23552564512B00C4A63D /* sdlsoundsource.cpp */; }; + 3BD2B42125651943003DAD8A /* serial-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23472564512A00C4A63D /* serial-util.h */; }; + 3BD2B42225651943003DAD8A /* serializable.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230D2564512300C4A63D /* serializable.h */; }; + 3BD2B42325651943003DAD8A /* settingsmenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23622564512D00C4A63D /* settingsmenu.cpp */; }; + 3BD2B42425651943003DAD8A /* settingsmenu.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23182564512400C4A63D /* settingsmenu.h */; }; + 3BD2B42525651943003DAD8A /* shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23582564512C00C4A63D /* shader.cpp */; }; + 3BD2B42625651943003DAD8A /* shader.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23122564512400C4A63D /* shader.h */; }; + 3BD2B42725651943003DAD8A /* sharedmidistate.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233D2564512900C4A63D /* sharedmidistate.h */; }; + 3BD2B42825651943003DAD8A /* sharedstate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23212564512600C4A63D /* sharedstate.cpp */; }; + 3BD2B42925651943003DAD8A /* sharedstate.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233B2564512900C4A63D /* sharedstate.h */; }; + 3BD2B42A25651943003DAD8A /* soundemitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236B2564512D00C4A63D /* soundemitter.cpp */; }; + 3BD2B42B25651943003DAD8A /* soundemitter.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23512564512B00C4A63D /* soundemitter.h */; }; + 3BD2B42C25651943003DAD8A /* sprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23142564512400C4A63D /* sprite.cpp */; }; + 3BD2B42D25651943003DAD8A /* sprite.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23642564512D00C4A63D /* sprite.h */; }; + 3BD2B42E25651943003DAD8A /* table.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23502564512B00C4A63D /* table.cpp */; }; + 3BD2B42F25651943003DAD8A /* table.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236E2564512E00C4A63D /* table.h */; }; + 3BD2B43025651943003DAD8A /* texpool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234C2564512B00C4A63D /* texpool.cpp */; }; + 3BD2B43125651943003DAD8A /* texpool.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23392564512900C4A63D /* texpool.h */; }; + 3BD2B43225651943003DAD8A /* tileatlas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23232564512600C4A63D /* tileatlas.cpp */; }; + 3BD2B43325651943003DAD8A /* tileatlas.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23452564512A00C4A63D /* tileatlas.h */; }; + 3BD2B43425651943003DAD8A /* tileatlasvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23572564512C00C4A63D /* tileatlasvx.cpp */; }; + 3BD2B43525651943003DAD8A /* tileatlasvx.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234D2564512B00C4A63D /* tileatlasvx.h */; }; + 3BD2B43625651943003DAD8A /* tilemap-common.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23332564512800C4A63D /* tilemap-common.h */; }; + 3BD2B43725651943003DAD8A /* tilemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23242564512600C4A63D /* tilemap.cpp */; }; + 3BD2B43825651943003DAD8A /* tilemap.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234F2564512B00C4A63D /* tilemap.h */; }; + 3BD2B43925651943003DAD8A /* tilemapvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23162564512400C4A63D /* tilemapvx.cpp */; }; + 3BD2B43A25651943003DAD8A /* tilemapvx.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230E2564512300C4A63D /* tilemapvx.h */; }; + 3BD2B43B25651943003DAD8A /* tilequad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23682564512D00C4A63D /* tilequad.cpp */; }; + 3BD2B43C25651943003DAD8A /* tilequad.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23172564512400C4A63D /* tilequad.h */; }; + 3BD2B43D25651943003DAD8A /* transform.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23292564512700C4A63D /* transform.h */; }; + 3BD2B43E25651943003DAD8A /* util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23362564512800C4A63D /* util.h */; }; + 3BD2B43F25651943003DAD8A /* vertex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23112564512400C4A63D /* vertex.cpp */; }; + 3BD2B44025651943003DAD8A /* vertex.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23312564512800C4A63D /* vertex.h */; }; + 3BD2B44125651943003DAD8A /* viewport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232D2564512700C4A63D /* viewport.cpp */; }; + 3BD2B44225651943003DAD8A /* viewport.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23442564512A00C4A63D /* viewport.h */; }; + 3BD2B44325651943003DAD8A /* vorbissource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235B2564512C00C4A63D /* vorbissource.cpp */; }; + 3BD2B44425651943003DAD8A /* window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23722564512E00C4A63D /* window.cpp */; }; + 3BD2B44525651943003DAD8A /* window.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233E2564512900C4A63D /* window.h */; }; + 3BD2B44625651943003DAD8A /* windowvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23522564512B00C4A63D /* windowvx.cpp */; }; + 3BD2B44725651943003DAD8A /* windowvx.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232F2564512700C4A63D /* windowvx.h */; }; + 3BD2B44825651952003DAD8A /* audio-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BA2564517400C4A63D /* audio-binding.cpp */; }; + 3BD2B44925651952003DAD8A /* binding-mri.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B02564517200C4A63D /* binding-mri.cpp */; }; + 3BD2B44A25651952003DAD8A /* binding-types.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A32564517100C4A63D /* binding-types.h */; }; + 3BD2B44B25651952003DAD8A /* binding-util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23C02564517500C4A63D /* binding-util.cpp */; }; + 3BD2B44C25651952003DAD8A /* binding-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B52564517300C4A63D /* binding-util.h */; }; + 3BD2B44D25651952003DAD8A /* bitmap-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AD2564517200C4A63D /* bitmap-binding.cpp */; }; + 3BD2B44E25651952003DAD8A /* cusl-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BC2564517400C4A63D /* cusl-binding.cpp */; }; + 3BD2B44F25651952003DAD8A /* disposable-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B92564517400C4A63D /* disposable-binding.h */; }; + 3BD2B45025651952003DAD8A /* etc-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AA2564517100C4A63D /* etc-binding.cpp */; }; + 3BD2B45125651952003DAD8A /* filesystem-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B72564517300C4A63D /* filesystem-binding.cpp */; }; + 3BD2B45225651952003DAD8A /* flashable-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B62564517300C4A63D /* flashable-binding.h */; }; + 3BD2B45325651952003DAD8A /* font-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AF2564517200C4A63D /* font-binding.cpp */; }; + 3BD2B45425651952003DAD8A /* graphics-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B22564517300C4A63D /* graphics-binding.cpp */; }; + 3BD2B45525651952003DAD8A /* input-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A92564517100C4A63D /* input-binding.cpp */; }; + 3BD2B45625651952003DAD8A /* miniffi-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B32564517300C4A63D /* miniffi-binding.cpp */; }; + 3BD2B45725651952003DAD8A /* module_rpg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BE2564517400C4A63D /* module_rpg.cpp */; }; + 3BD2B45825651952003DAD8A /* plane-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BF2564517400C4A63D /* plane-binding.cpp */; }; + 3BD2B45925651952003DAD8A /* sceneelement-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BD2564517400C4A63D /* sceneelement-binding.h */; }; + 3BD2B45A25651952003DAD8A /* serializable-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AB2564517200C4A63D /* serializable-binding.h */; }; + 3BD2B45B25651952003DAD8A /* sprite-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A72564517100C4A63D /* sprite-binding.cpp */; }; + 3BD2B45C25651952003DAD8A /* table-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A22564517100C4A63D /* table-binding.cpp */; }; + 3BD2B45D25651952003DAD8A /* tilemap-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A52564517100C4A63D /* tilemap-binding.cpp */; }; + 3BD2B45E25651952003DAD8A /* tilemapvx-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B82564517300C4A63D /* tilemapvx-binding.cpp */; }; + 3BD2B45F25651952003DAD8A /* viewport-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B42564517300C4A63D /* viewport-binding.cpp */; }; + 3BD2B46025651952003DAD8A /* viewportelement-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BB2564517400C4A63D /* viewportelement-binding.h */; }; + 3BD2B46125651952003DAD8A /* window-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A82564517100C4A63D /* window-binding.cpp */; }; + 3BD2B46225651952003DAD8A /* windowvx-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A42564517100C4A63D /* windowvx-binding.cpp */; }; + 3BD2B46325651B73003DAD8A /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3BDB23E22564546E00C4A63D /* icon.icns */; }; + 3BD2B46B25651DE2003DAD8A /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46A25651C2D003DAD8A /* OpenAL.framework */; }; + 3BD2B46C25651DE6003DAD8A /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46525651BF3003DAD8A /* libz.tbd */; }; + 3BD2B46D25651DE8003DAD8A /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46825651C12003DAD8A /* libiconv.tbd */; }; + 3BD2B46E25651DF8003DAD8A /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46625651BFC003DAD8A /* CoreFoundation.framework */; }; + 3BD2B46F25651DFA003DAD8A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46725651C00003DAD8A /* Foundation.framework */; }; + 3BD2B47B256534C0003DAD8A /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B47A256534BA003DAD8A /* IOKit.framework */; }; + 3BD2B4F9256558B6003DAD8A /* CocoaHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BD2B4F7256558B6003DAD8A /* CocoaHelpers.mm */; }; + 3BD2B64D2565AEC0003DAD8A /* audio-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BA2564517400C4A63D /* audio-binding.cpp */; }; + 3BD2B64E2565AEC0003DAD8A /* binding-mri.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B02564517200C4A63D /* binding-mri.cpp */; }; + 3BD2B64F2565AEC0003DAD8A /* binding-types.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A32564517100C4A63D /* binding-types.h */; }; + 3BD2B6502565AEC0003DAD8A /* binding-util.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23C02564517500C4A63D /* binding-util.cpp */; }; + 3BD2B6512565AEC0003DAD8A /* binding-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B52564517300C4A63D /* binding-util.h */; }; + 3BD2B6522565AEC0003DAD8A /* bitmap-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AD2564517200C4A63D /* bitmap-binding.cpp */; }; + 3BD2B6532565AEC0003DAD8A /* cusl-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BC2564517400C4A63D /* cusl-binding.cpp */; }; + 3BD2B6542565AEC0003DAD8A /* disposable-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B92564517400C4A63D /* disposable-binding.h */; }; + 3BD2B6552565AEC0003DAD8A /* etc-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AA2564517100C4A63D /* etc-binding.cpp */; }; + 3BD2B6562565AEC0003DAD8A /* filesystem-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B72564517300C4A63D /* filesystem-binding.cpp */; }; + 3BD2B6572565AEC0003DAD8A /* flashable-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B62564517300C4A63D /* flashable-binding.h */; }; + 3BD2B6582565AEC0003DAD8A /* font-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AF2564517200C4A63D /* font-binding.cpp */; }; + 3BD2B6592565AEC0003DAD8A /* graphics-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B22564517300C4A63D /* graphics-binding.cpp */; }; + 3BD2B65A2565AEC0003DAD8A /* input-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A92564517100C4A63D /* input-binding.cpp */; }; + 3BD2B65B2565AEC0003DAD8A /* miniffi-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B32564517300C4A63D /* miniffi-binding.cpp */; }; + 3BD2B65C2565AEC0003DAD8A /* module_rpg.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BE2564517400C4A63D /* module_rpg.cpp */; }; + 3BD2B65D2565AEC0003DAD8A /* plane-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BF2564517400C4A63D /* plane-binding.cpp */; }; + 3BD2B65E2565AEC0003DAD8A /* sceneelement-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BD2564517400C4A63D /* sceneelement-binding.h */; }; + 3BD2B65F2565AEC0003DAD8A /* serializable-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23AB2564517200C4A63D /* serializable-binding.h */; }; + 3BD2B6602565AEC0003DAD8A /* sprite-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A72564517100C4A63D /* sprite-binding.cpp */; }; + 3BD2B6612565AEC0003DAD8A /* table-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A22564517100C4A63D /* table-binding.cpp */; }; + 3BD2B6622565AEC0003DAD8A /* tilemap-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A52564517100C4A63D /* tilemap-binding.cpp */; }; + 3BD2B6632565AEC0003DAD8A /* tilemapvx-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B82564517300C4A63D /* tilemapvx-binding.cpp */; }; + 3BD2B6642565AEC0003DAD8A /* viewport-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23B42564517300C4A63D /* viewport-binding.cpp */; }; + 3BD2B6652565AEC0003DAD8A /* viewportelement-binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23BB2564517400C4A63D /* viewportelement-binding.h */; }; + 3BD2B6662565AEC0003DAD8A /* window-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A82564517100C4A63D /* window-binding.cpp */; }; + 3BD2B6672565AEC0003DAD8A /* windowvx-binding.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23A42564517100C4A63D /* windowvx-binding.cpp */; }; + 3BD2B6682565AEC0003DAD8A /* al-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23462564512A00C4A63D /* al-util.h */; }; + 3BD2B6692565AEC0003DAD8A /* aldatasource.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231C2564512500C4A63D /* aldatasource.h */; }; + 3BD2B66A2565AEC0003DAD8A /* alstream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235E2564512C00C4A63D /* alstream.cpp */; }; + 3BD2B66B2565AEC0003DAD8A /* alstream.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234B2564512B00C4A63D /* alstream.h */; }; + 3BD2B66C2565AEC0003DAD8A /* audio.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23152564512400C4A63D /* audio.cpp */; }; + 3BD2B66D2565AEC0003DAD8A /* audio.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235A2564512C00C4A63D /* audio.h */; }; + 3BD2B66E2565AEC0003DAD8A /* audiostream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23432564512A00C4A63D /* audiostream.cpp */; }; + 3BD2B66F2565AEC0003DAD8A /* audiostream.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23532564512B00C4A63D /* audiostream.h */; }; + 3BD2B6702565AEC0003DAD8A /* autotiles.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23412564512900C4A63D /* autotiles.cpp */; }; + 3BD2B6712565AEC0003DAD8A /* autotilesvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23602564512D00C4A63D /* autotilesvx.cpp */; }; + 3BD2B6722565AEC0003DAD8A /* binding.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233C2564512900C4A63D /* binding.h */; }; + 3BD2B6732565AEC0003DAD8A /* bitmap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233F2564512900C4A63D /* bitmap.cpp */; }; + 3BD2B6742565AEC0003DAD8A /* bitmap.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232A2564512700C4A63D /* bitmap.h */; }; + 3BD2B6752565AEC0003DAD8A /* boost-hash.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236F2564512E00C4A63D /* boost-hash.h */; }; + 3BD2B6762565AEC0003DAD8A /* config.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236D2564512E00C4A63D /* config.h */; }; + 3BD2B6772565AEC0003DAD8A /* config.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23662564512D00C4A63D /* config.mm */; }; + 3BD2B6782565AEC0003DAD8A /* debugwriter.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23282564512600C4A63D /* debugwriter.h */; }; + 3BD2B6792565AEC0003DAD8A /* disposable.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23102564512400C4A63D /* disposable.h */; }; + 3BD2B67A2565AEC0003DAD8A /* CocoaHelpers.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BD2B4F7256558B6003DAD8A /* CocoaHelpers.mm */; }; + 3BD2B67B2565AEC0003DAD8A /* etc-internal.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232B2564512700C4A63D /* etc-internal.h */; }; + 3BD2B67C2565AEC0003DAD8A /* etc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236C2564512D00C4A63D /* etc.cpp */; }; + 3BD2B67D2565AEC0003DAD8A /* etc.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235D2564512C00C4A63D /* etc.h */; }; + 3BD2B67E2565AEC0003DAD8A /* eventthread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231D2564512500C4A63D /* eventthread.cpp */; }; + 3BD2B67F2565AEC0003DAD8A /* eventthread.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23252564512600C4A63D /* eventthread.h */; }; + 3BD2B6802565AEC0003DAD8A /* exception.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23692564512D00C4A63D /* exception.h */; }; + 3BD2B6812565AEC0003DAD8A /* fake-api.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233A2564512900C4A63D /* fake-api.h */; }; + 3BD2B6822565AEC0003DAD8A /* fake-api.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23322564512800C4A63D /* fake-api.mm */; }; + 3BD2B6832565AEC0003DAD8A /* filesystem.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230C2564512300C4A63D /* filesystem.h */; }; + 3BD2B6842565AEC0003DAD8A /* filesystem.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231E2564512500C4A63D /* filesystem.mm */; }; + 3BD2B6852565AEC0003DAD8A /* flashable.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23422564512A00C4A63D /* flashable.h */; }; + 3BD2B6862565AEC0003DAD8A /* fluid-fun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234E2564512B00C4A63D /* fluid-fun.cpp */; }; + 3BD2B6872565AEC0003DAD8A /* fluid-fun.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230F2564512400C4A63D /* fluid-fun.h */; }; + 3BD2B6882565AEC0003DAD8A /* font.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23262564512600C4A63D /* font.cpp */; }; + 3BD2B6892565AEC0003DAD8A /* font.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23352564512800C4A63D /* font.h */; }; + 3BD2B68A2565AEC0003DAD8A /* gl-debug.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23672564512D00C4A63D /* gl-debug.cpp */; }; + 3BD2B68B2565AEC0003DAD8A /* gl-debug.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23132564512400C4A63D /* gl-debug.h */; }; + 3BD2B68C2565AEC0003DAD8A /* gl-fun.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23402564512900C4A63D /* gl-fun.cpp */; }; + 3BD2B68D2565AEC0003DAD8A /* gl-fun.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231F2564512500C4A63D /* gl-fun.h */; }; + 3BD2B68E2565AEC0003DAD8A /* gl-meta.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23482564512B00C4A63D /* gl-meta.cpp */; }; + 3BD2B68F2565AEC0003DAD8A /* gl-meta.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23612564512D00C4A63D /* gl-meta.h */; }; + 3BD2B6902565AEC0003DAD8A /* gl-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236A2564512D00C4A63D /* gl-util.h */; }; + 3BD2B6912565AEC0003DAD8A /* global-ibo.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23632564512D00C4A63D /* global-ibo.h */; }; + 3BD2B6922565AEC0003DAD8A /* glstate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23712564512E00C4A63D /* glstate.cpp */; }; + 3BD2B6932565AEC0003DAD8A /* glstate.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23562564512C00C4A63D /* glstate.h */; }; + 3BD2B6942565AEC0003DAD8A /* graphics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231A2564512500C4A63D /* graphics.cpp */; }; + 3BD2B6952565AEC0003DAD8A /* graphics.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23192564512500C4A63D /* graphics.h */; }; + 3BD2B6962565AEC0003DAD8A /* input.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23372564512800C4A63D /* input.cpp */; }; + 3BD2B6972565AEC0003DAD8A /* input.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23302564512700C4A63D /* input.h */; }; + 3BD2B6982565AEC0003DAD8A /* intrulist.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235F2564512C00C4A63D /* intrulist.h */; }; + 3BD2B6992565AEC0003DAD8A /* keybindings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232E2564512700C4A63D /* keybindings.cpp */; }; + 3BD2B69A2565AEC0003DAD8A /* keybindings.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23492564512B00C4A63D /* keybindings.h */; }; + 3BD2B69B2565AEC0003DAD8A /* lang-fun.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235C2564512C00C4A63D /* lang-fun.h */; }; + 3BD2B69C2565AEC0003DAD8A /* lang-fun.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23222564512600C4A63D /* lang-fun.mm */; }; + 3BD2B69D2565AEC0003DAD8A /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23702564512E00C4A63D /* main.mm */; }; + 3BD2B69E2565AEC0003DAD8A /* midisource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23382564512800C4A63D /* midisource.cpp */; }; + 3BD2B69F2565AEC0003DAD8A /* plane.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232C2564512700C4A63D /* plane.cpp */; }; + 3BD2B6A02565AEC0003DAD8A /* plane.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23652564512D00C4A63D /* plane.h */; }; + 3BD2B6A12565AEC0003DAD8A /* quad.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23272564512600C4A63D /* quad.h */; }; + 3BD2B6A22565AEC0003DAD8A /* quadarray.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230B2564512300C4A63D /* quadarray.h */; }; + 3BD2B6A32565AEC0003DAD8A /* rgssad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234A2564512B00C4A63D /* rgssad.cpp */; }; + 3BD2B6A42565AEC0003DAD8A /* rgssad.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23202564512500C4A63D /* rgssad.h */; }; + 3BD2B6A52565AEC0003DAD8A /* scene.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23542564512B00C4A63D /* scene.cpp */; }; + 3BD2B6A62565AEC0003DAD8A /* scene.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23342564512800C4A63D /* scene.h */; }; + 3BD2B6A72565AEC0003DAD8A /* sdl-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB231B2564512500C4A63D /* sdl-util.h */; }; + 3BD2B6A82565AEC0003DAD8A /* sdlsoundsource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23552564512B00C4A63D /* sdlsoundsource.cpp */; }; + 3BD2B6A92565AEC0003DAD8A /* serial-util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23472564512A00C4A63D /* serial-util.h */; }; + 3BD2B6AA2565AEC0003DAD8A /* serializable.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230D2564512300C4A63D /* serializable.h */; }; + 3BD2B6AB2565AEC0003DAD8A /* settingsmenu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23622564512D00C4A63D /* settingsmenu.cpp */; }; + 3BD2B6AC2565AEC0003DAD8A /* settingsmenu.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23182564512400C4A63D /* settingsmenu.h */; }; + 3BD2B6AD2565AEC0003DAD8A /* shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23582564512C00C4A63D /* shader.cpp */; }; + 3BD2B6AE2565AEC0003DAD8A /* shader.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23122564512400C4A63D /* shader.h */; }; + 3BD2B6AF2565AEC0003DAD8A /* sharedmidistate.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233D2564512900C4A63D /* sharedmidistate.h */; }; + 3BD2B6B02565AEC0003DAD8A /* sharedstate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23212564512600C4A63D /* sharedstate.cpp */; }; + 3BD2B6B12565AEC0003DAD8A /* sharedstate.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233B2564512900C4A63D /* sharedstate.h */; }; + 3BD2B6B22565AEC0003DAD8A /* soundemitter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236B2564512D00C4A63D /* soundemitter.cpp */; }; + 3BD2B6B32565AEC0003DAD8A /* soundemitter.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23512564512B00C4A63D /* soundemitter.h */; }; + 3BD2B6B42565AEC0003DAD8A /* sprite.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23142564512400C4A63D /* sprite.cpp */; }; + 3BD2B6B52565AEC0003DAD8A /* sprite.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23642564512D00C4A63D /* sprite.h */; }; + 3BD2B6B62565AEC0003DAD8A /* table.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23502564512B00C4A63D /* table.cpp */; }; + 3BD2B6B72565AEC0003DAD8A /* table.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB236E2564512E00C4A63D /* table.h */; }; + 3BD2B6B82565AEC0003DAD8A /* texpool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234C2564512B00C4A63D /* texpool.cpp */; }; + 3BD2B6B92565AEC0003DAD8A /* texpool.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23392564512900C4A63D /* texpool.h */; }; + 3BD2B6BA2565AEC0003DAD8A /* tileatlas.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23232564512600C4A63D /* tileatlas.cpp */; }; + 3BD2B6BB2565AEC0003DAD8A /* tileatlas.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23452564512A00C4A63D /* tileatlas.h */; }; + 3BD2B6BC2565AEC0003DAD8A /* tileatlasvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23572564512C00C4A63D /* tileatlasvx.cpp */; }; + 3BD2B6BD2565AEC0003DAD8A /* tileatlasvx.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234D2564512B00C4A63D /* tileatlasvx.h */; }; + 3BD2B6BE2565AEC0003DAD8A /* tilemap-common.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23332564512800C4A63D /* tilemap-common.h */; }; + 3BD2B6BF2565AEC0003DAD8A /* tilemap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23242564512600C4A63D /* tilemap.cpp */; }; + 3BD2B6C02565AEC0003DAD8A /* tilemap.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB234F2564512B00C4A63D /* tilemap.h */; }; + 3BD2B6C12565AEC0003DAD8A /* tilemapvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23162564512400C4A63D /* tilemapvx.cpp */; }; + 3BD2B6C22565AEC0003DAD8A /* tilemapvx.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB230E2564512300C4A63D /* tilemapvx.h */; }; + 3BD2B6C32565AEC0003DAD8A /* tilequad.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23682564512D00C4A63D /* tilequad.cpp */; }; + 3BD2B6C42565AEC0003DAD8A /* tilequad.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23172564512400C4A63D /* tilequad.h */; }; + 3BD2B6C52565AEC0003DAD8A /* transform.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23292564512700C4A63D /* transform.h */; }; + 3BD2B6C62565AEC0003DAD8A /* util.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23362564512800C4A63D /* util.h */; }; + 3BD2B6C72565AEC0003DAD8A /* vertex.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23112564512400C4A63D /* vertex.cpp */; }; + 3BD2B6C82565AEC0003DAD8A /* vertex.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23312564512800C4A63D /* vertex.h */; }; + 3BD2B6C92565AEC0003DAD8A /* viewport.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232D2564512700C4A63D /* viewport.cpp */; }; + 3BD2B6CA2565AEC0003DAD8A /* viewport.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23442564512A00C4A63D /* viewport.h */; }; + 3BD2B6CB2565AEC0003DAD8A /* vorbissource.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB235B2564512C00C4A63D /* vorbissource.cpp */; }; + 3BD2B6CC2565AEC0003DAD8A /* window.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23722564512E00C4A63D /* window.cpp */; }; + 3BD2B6CD2565AEC0003DAD8A /* window.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB233E2564512900C4A63D /* window.h */; }; + 3BD2B6CE2565AEC0003DAD8A /* windowvx.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB23522564512B00C4A63D /* windowvx.cpp */; }; + 3BD2B6CF2565AEC0003DAD8A /* windowvx.h in Sources */ = {isa = PBXBuildFile; fileRef = 3BDB232F2564512700C4A63D /* windowvx.h */; }; + 3BD2B6E72565AEC0003DAD8A /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B47A256534BA003DAD8A /* IOKit.framework */; }; + 3BD2B6ED2565AEC0003DAD8A /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46825651C12003DAD8A /* libiconv.tbd */; }; + 3BD2B6EE2565AEC0003DAD8A /* OpenAL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46A25651C2D003DAD8A /* OpenAL.framework */; }; + 3BD2B6EF2565AEC0003DAD8A /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46625651BFC003DAD8A /* CoreFoundation.framework */; }; + 3BD2B6F12565AEC0003DAD8A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46725651C00003DAD8A /* Foundation.framework */; }; + 3BD2B6F32565AEC0003DAD8A /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3BD2B46525651BF3003DAD8A /* libz.tbd */; }; + 3BD2B6F92565AEC0003DAD8A /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3BDB23E22564546E00C4A63D /* icon.icns */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 3BA08E94256631FD00449CFF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3BDB22EC25644FBF00C4A63D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3BA08E902566314300449CFF; + remoteInfo = gen; + }; + 3BA08E962566320100449CFF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3BDB22EC25644FBF00C4A63D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3BA08E902566314300449CFF; + remoteInfo = gen; + }; + 3BA08ED0256643A600449CFF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3BDB22EC25644FBF00C4A63D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3BA08EA3256641ED00449CFF; + remoteInfo = "mkxpz-resources"; + }; + 3BA08ED2256643AD00449CFF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 3BDB22EC25644FBF00C4A63D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 3BA08EA3256641ED00449CFF; + remoteInfo = "mkxpz-resources"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 3BA08EAC2566426200449CFF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Shaders; + dstSubfolderSpec = 7; + files = ( + 3BA08EAD256642A300449CFF /* bitmapBlit.frag in CopyFiles */, + 3BA08EAE256642A300449CFF /* blur.frag in CopyFiles */, + 3BA08EAF256642A300449CFF /* blurH.vert in CopyFiles */, + 3BA08EB0256642A300449CFF /* blurV.vert in CopyFiles */, + 3BA08EB1256642A300449CFF /* common.h in CopyFiles */, + 3BA08EB2256642A300449CFF /* flashMap.frag in CopyFiles */, + 3BA08EB3256642A300449CFF /* flatColor.frag in CopyFiles */, + 3BA08EB4256642A300449CFF /* gray.frag in CopyFiles */, + 3BA08EB5256642A300449CFF /* hue.frag in CopyFiles */, + 3BA08EB6256642A300449CFF /* minimal.vert in CopyFiles */, + 3BA08EB7256642A300449CFF /* plane.frag in CopyFiles */, + 3BA08EB8256642A300449CFF /* simple.frag in CopyFiles */, + 3BA08EB9256642A300449CFF /* simple.vert in CopyFiles */, + 3BA08EBA256642A300449CFF /* simpleAlpha.frag in CopyFiles */, + 3BA08EBB256642A300449CFF /* simpleAlphaUni.frag in CopyFiles */, + 3BA08EBC256642A300449CFF /* simpleColor.frag in CopyFiles */, + 3BA08EBD256642A300449CFF /* simpleColor.vert in CopyFiles */, + 3BA08EBE256642A300449CFF /* simpleMatrix.vert in CopyFiles */, + 3BA08EBF256642A300449CFF /* sprite.frag in CopyFiles */, + 3BA08EC0256642A300449CFF /* sprite.vert in CopyFiles */, + 3BA08EC1256642A300449CFF /* tilemap.frag in CopyFiles */, + 3BA08EC2256642A300449CFF /* tilemap.vert in CopyFiles */, + 3BA08EC3256642A300449CFF /* tilemapvx.vert in CopyFiles */, + 3BA08EC4256642A300449CFF /* trans.frag in CopyFiles */, + 3BA08EC5256642A300449CFF /* transSimple.frag in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BA08EC8256642C900449CFF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = Fonts; + dstSubfolderSpec = 7; + files = ( + 3BA08EC92566432900449CFF /* wqymicrohei.ttf in CopyFiles */, + 3BA08ECA2566432900449CFF /* liberation.ttf in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BA08EE12566499400449CFF /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = BindingScripts; + dstSubfolderSpec = 7; + files = ( + 3BA08EE22566499C00449CFF /* EssentialsCompatibility.rb in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BD2B3B8256518DD003DAD8A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3BA08E8925661E3500449CFF /* SDL2.framework in Embed Frameworks */, + 3BA08E8D25661E4300449CFF /* libfluidsynth.3.dylib in Embed Frameworks */, + 3BA08E8725661E3500449CFF /* SDL2_ttf.framework in Embed Frameworks */, + 3BA08E8525661E3500449CFF /* SDL2_image.framework in Embed Frameworks */, + 3BA08E8C25661E4100449CFF /* libruby.1.8.7.dylib in Embed Frameworks */, + 3BA08E8325661E3400449CFF /* Ogg.framework in Embed Frameworks */, + 3BA08E8125661E3400449CFF /* MetalANGLE.framework in Embed Frameworks */, + 3BA08E8B25661E3500449CFF /* Vorbis.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 3BD2B6FA2565AEC0003DAD8A /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 3BA08E6125661C4800449CFF /* SDL2.framework in Embed Frameworks */, + 3BA08E7F25661D5600449CFF /* libruby.2.7.dylib in Embed Frameworks */, + 3BA08E5B25661C4700449CFF /* SDL2_ttf.framework in Embed Frameworks */, + 3BA08E5D25661C4800449CFF /* MetalANGLE.framework in Embed Frameworks */, + 3BA08E6325661C4800449CFF /* SDL2_image.framework in Embed Frameworks */, + 3BA08E6725661CAA00449CFF /* libfluidsynth.3.dylib in Embed Frameworks */, + 3BA08E5F25661C4800449CFF /* Ogg.framework in Embed Frameworks */, + 3BA08E5925661C4700449CFF /* Vorbis.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 3BA08E5225661C4600449CFF /* Vorbis.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Vorbis.framework; path = "MKXPZ-Dependencies/mac/frameworks/Vorbis.framework"; sourceTree = ""; }; + 3BA08E5325661C4700449CFF /* SDL2_ttf.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2_ttf.framework; path = "MKXPZ-Dependencies/mac/frameworks/SDL2_ttf.framework"; sourceTree = ""; }; + 3BA08E5425661C4700449CFF /* MetalANGLE.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalANGLE.framework; path = "MKXPZ-Dependencies/mac/frameworks/MetalANGLE.framework"; sourceTree = ""; }; + 3BA08E5525661C4700449CFF /* Ogg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Ogg.framework; path = "MKXPZ-Dependencies/mac/frameworks/Ogg.framework"; sourceTree = ""; }; + 3BA08E5625661C4700449CFF /* SDL2.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2.framework; path = "MKXPZ-Dependencies/mac/frameworks/SDL2.framework"; sourceTree = ""; }; + 3BA08E5725661C4700449CFF /* SDL2_image.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SDL2_image.framework; path = "MKXPZ-Dependencies/mac/frameworks/SDL2_image.framework"; sourceTree = ""; }; + 3BA08E6425661CA600449CFF /* libfluidsynth.3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libfluidsynth.3.dylib; path = "MKXPZ-Dependencies/mac/prefix/lib64/libfluidsynth.3.dylib"; sourceTree = ""; }; + 3BA08E6925661CD900449CFF /* libphysfs.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libphysfs.a; path = "MKXPZ-Dependencies/mac/prefix/lib/libphysfs.a"; sourceTree = ""; }; + 3BA08E6A25661CD900449CFF /* libobjfwbridge.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libobjfwbridge.a; path = "MKXPZ-Dependencies/mac/prefix/lib/libobjfwbridge.a"; sourceTree = ""; }; + 3BA08E6B25661CD900449CFF /* libobjfw.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libobjfw.a; path = "MKXPZ-Dependencies/mac/prefix/lib/libobjfw.a"; sourceTree = ""; }; + 3BA08E6C25661CD900449CFF /* libsigc-2.0.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libsigc-2.0.a"; path = "MKXPZ-Dependencies/mac/prefix/lib/libsigc-2.0.a"; sourceTree = ""; }; + 3BA08E6D25661CD900449CFF /* libpixman-1.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libpixman-1.a"; path = "MKXPZ-Dependencies/mac/prefix/lib/libpixman-1.a"; sourceTree = ""; }; + 3BA08E7B25661D2C00449CFF /* libruby.1.8.7.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libruby.1.8.7.dylib; path = "MKXPZ-Dependencies/mac/prefix/opt/ruby1.8.7/lib/libruby.1.8.7.dylib"; sourceTree = ""; }; + 3BA08E7D25661D3700449CFF /* libruby.2.7.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libruby.2.7.dylib; path = "MKXPZ-Dependencies/mac/prefix/opt/ruby2.7.0/lib/libruby.2.7.dylib"; sourceTree = ""; }; + 3BA08EA4256641ED00449CFF /* Assets.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Assets.bundle; sourceTree = BUILT_PRODUCTS_DIR; }; + 3BA08EA6256641EE00449CFF /* Assets.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Assets.plist; sourceTree = ""; }; + 3BA08EE725664BCC00449CFF /* SDL_sound_shn.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_shn.c; sourceTree = ""; }; + 3BA08EE825664BCC00449CFF /* SDL_sound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sound.h; sourceTree = ""; }; + 3BA08EE925664BCC00449CFF /* SDL_sound_coreaudio.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_coreaudio.c; sourceTree = ""; }; + 3BA08EEA25664BCC00449CFF /* SDL_sound_skeleton.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_skeleton.c; sourceTree = ""; }; + 3BA08EEB25664BCC00449CFF /* SDL_sound_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_sound_internal.h; sourceTree = ""; }; + 3BA08EEC25664BCC00449CFF /* SDL_sound_raw.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_raw.c; sourceTree = ""; }; + 3BA08EED25664BCC00449CFF /* SDL_sound_modplug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_modplug.c; sourceTree = ""; }; + 3BA08EEE25664BCC00449CFF /* dr_mp3.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dr_mp3.h; sourceTree = ""; }; + 3BA08EEF25664BCC00449CFF /* SDL_sound_vorbis.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_vorbis.c; sourceTree = ""; }; + 3BA08EF025664BCC00449CFF /* SDL_sound.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound.c; sourceTree = ""; }; + 3BA08EF125664BCC00449CFF /* SDL_sound_voc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_voc.c; sourceTree = ""; }; + 3BA08EF225664BCC00449CFF /* dr_flac.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dr_flac.h; sourceTree = ""; }; + 3BA08EF325664BCC00449CFF /* SDL_sound_wav.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_wav.c; sourceTree = ""; }; + 3BA08EF425664BCC00449CFF /* SDL_sound_au.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_au.c; sourceTree = ""; }; + 3BA08EF525664BCC00449CFF /* SDL_sound_mp3.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_mp3.c; sourceTree = ""; }; + 3BA08EF625664BCC00449CFF /* SDL_sound_aiff.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_aiff.c; sourceTree = ""; }; + 3BA08EF725664BCC00449CFF /* SDL_sound_flac.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_sound_flac.c; sourceTree = ""; }; + 3BA08EF825664BCC00449CFF /* stb_vorbis.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = stb_vorbis.h; sourceTree = ""; }; + 3BD2B3AF25651885003DAD8A /* entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = entitlements.plist; sourceTree = ""; }; + 3BD2B46525651BF3003DAD8A /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = usr/lib/libz.tbd; sourceTree = SDKROOT; }; + 3BD2B46625651BFC003DAD8A /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 3BD2B46725651C00003DAD8A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 3BD2B46825651C12003DAD8A /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = usr/lib/libiconv.tbd; sourceTree = SDKROOT; }; + 3BD2B46925651C1B003DAD8A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 3BD2B46A25651C2D003DAD8A /* OpenAL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenAL.framework; path = System/Library/Frameworks/OpenAL.framework; sourceTree = SDKROOT; }; + 3BD2B47A256534BA003DAD8A /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = System/Library/Frameworks/IOKit.framework; sourceTree = SDKROOT; }; + 3BD2B4AC25654AD6003DAD8A /* blur.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = blur.frag; path = ../shader/blur.frag; sourceTree = ""; }; + 3BD2B4AD25654AD6003DAD8A /* simpleMatrix.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = simpleMatrix.vert; path = ../shader/simpleMatrix.vert; sourceTree = ""; }; + 3BD2B4AE25654AD6003DAD8A /* gray.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = gray.frag; path = ../shader/gray.frag; sourceTree = ""; }; + 3BD2B4AF25654AD6003DAD8A /* simple.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = simple.frag; path = ../shader/simple.frag; sourceTree = ""; }; + 3BD2B4B025654AD6003DAD8A /* blurV.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = blurV.vert; path = ../shader/blurV.vert; sourceTree = ""; }; + 3BD2B4B125654AD6003DAD8A /* sprite.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = sprite.vert; path = ../shader/sprite.vert; sourceTree = ""; }; + 3BD2B4B225654AD6003DAD8A /* tilemap.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = tilemap.vert; path = ../shader/tilemap.vert; sourceTree = ""; }; + 3BD2B4B325654AD6003DAD8A /* minimal.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = minimal.vert; path = ../shader/minimal.vert; sourceTree = ""; }; + 3BD2B4B425654AD6003DAD8A /* trans.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = trans.frag; path = ../shader/trans.frag; sourceTree = ""; }; + 3BD2B4B525654AD6003DAD8A /* simple.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = simple.vert; path = ../shader/simple.vert; sourceTree = ""; }; + 3BD2B4B625654AD6003DAD8A /* plane.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = plane.frag; path = ../shader/plane.frag; sourceTree = ""; }; + 3BD2B4B725654AD7003DAD8A /* flatColor.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = flatColor.frag; path = ../shader/flatColor.frag; sourceTree = ""; }; + 3BD2B4B825654AD7003DAD8A /* blurH.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = blurH.vert; path = ../shader/blurH.vert; sourceTree = ""; }; + 3BD2B4B925654AD7003DAD8A /* simpleAlphaUni.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = simpleAlphaUni.frag; path = ../shader/simpleAlphaUni.frag; sourceTree = ""; }; + 3BD2B4BA25654AD7003DAD8A /* transSimple.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = transSimple.frag; path = ../shader/transSimple.frag; sourceTree = ""; }; + 3BD2B4BB25654AD7003DAD8A /* bitmapBlit.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = bitmapBlit.frag; path = ../shader/bitmapBlit.frag; sourceTree = ""; }; + 3BD2B4BC25654AD7003DAD8A /* common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = common.h; path = ../shader/common.h; sourceTree = ""; }; + 3BD2B4BD25654AD7003DAD8A /* simpleAlpha.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = simpleAlpha.frag; path = ../shader/simpleAlpha.frag; sourceTree = ""; }; + 3BD2B4BE25654AD8003DAD8A /* simpleColor.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = simpleColor.vert; path = ../shader/simpleColor.vert; sourceTree = ""; }; + 3BD2B4BF25654AD8003DAD8A /* simpleColor.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = simpleColor.frag; path = ../shader/simpleColor.frag; sourceTree = ""; }; + 3BD2B4C025654AD8003DAD8A /* tilemapvx.vert */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = tilemapvx.vert; path = ../shader/tilemapvx.vert; sourceTree = ""; }; + 3BD2B4C125654AD8003DAD8A /* tilemap.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = tilemap.frag; path = ../shader/tilemap.frag; sourceTree = ""; }; + 3BD2B4C225654AD8003DAD8A /* sprite.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = sprite.frag; path = ../shader/sprite.frag; sourceTree = ""; }; + 3BD2B4C325654AD8003DAD8A /* hue.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = hue.frag; path = ../shader/hue.frag; sourceTree = ""; }; + 3BD2B4C425654AD8003DAD8A /* flashMap.frag */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.glsl; name = flashMap.frag; path = ../shader/flashMap.frag; sourceTree = ""; }; + 3BD2B4C625654B6F003DAD8A /* EssentialsCompatibility.rb */ = {isa = PBXFileReference; lastKnownFileType = text.script.ruby; name = EssentialsCompatibility.rb; path = ../scripts/EssentialsCompatibility.rb; sourceTree = ""; }; + 3BD2B4EE25654DC7003DAD8A /* icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = icon.png; path = ../assets/icon.png; sourceTree = ""; }; + 3BD2B4F025654DDB003DAD8A /* liberation.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = liberation.ttf; path = ../assets/liberation.ttf; sourceTree = ""; }; + 3BD2B4F125654DE3003DAD8A /* wqymicrohei.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = wqymicrohei.ttf; path = ../assets/wqymicrohei.ttf; sourceTree = ""; }; + 3BD2B4F7256558B6003DAD8A /* CocoaHelpers.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CocoaHelpers.mm; sourceTree = ""; }; + 3BD2B4F8256558B6003DAD8A /* CocoaHelpers.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CocoaHelpers.hpp; sourceTree = ""; }; + 3BD2B7252565AEC0003DAD8A /* mkxp-z.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "mkxp-z.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3BD2B7272565B343003DAD8A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + 3BD2B7282565B35D003DAD8A /* mkxp.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = mkxp.json; path = ../mkxp.json; sourceTree = ""; }; + 3BDB230B2564512300C4A63D /* quadarray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = quadarray.h; path = ../src/quadarray.h; sourceTree = ""; }; + 3BDB230C2564512300C4A63D /* filesystem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = filesystem.h; path = ../src/filesystem.h; sourceTree = ""; }; + 3BDB230D2564512300C4A63D /* serializable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = serializable.h; path = ../src/serializable.h; sourceTree = ""; }; + 3BDB230E2564512300C4A63D /* tilemapvx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tilemapvx.h; path = ../src/tilemapvx.h; sourceTree = ""; }; + 3BDB230F2564512400C4A63D /* fluid-fun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "fluid-fun.h"; path = "../src/fluid-fun.h"; sourceTree = ""; }; + 3BDB23102564512400C4A63D /* disposable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = disposable.h; path = ../src/disposable.h; sourceTree = ""; }; + 3BDB23112564512400C4A63D /* vertex.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = vertex.cpp; path = ../src/vertex.cpp; sourceTree = ""; }; + 3BDB23122564512400C4A63D /* shader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = shader.h; path = ../src/shader.h; sourceTree = ""; }; + 3BDB23132564512400C4A63D /* gl-debug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "gl-debug.h"; path = "../src/gl-debug.h"; sourceTree = ""; }; + 3BDB23142564512400C4A63D /* sprite.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sprite.cpp; path = ../src/sprite.cpp; sourceTree = ""; }; + 3BDB23152564512400C4A63D /* audio.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = audio.cpp; path = ../src/audio.cpp; sourceTree = ""; }; + 3BDB23162564512400C4A63D /* tilemapvx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tilemapvx.cpp; path = ../src/tilemapvx.cpp; sourceTree = ""; }; + 3BDB23172564512400C4A63D /* tilequad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tilequad.h; path = ../src/tilequad.h; sourceTree = ""; }; + 3BDB23182564512400C4A63D /* settingsmenu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = settingsmenu.h; path = ../src/settingsmenu.h; sourceTree = ""; }; + 3BDB23192564512500C4A63D /* graphics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = graphics.h; path = ../src/graphics.h; sourceTree = ""; }; + 3BDB231A2564512500C4A63D /* graphics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = graphics.cpp; path = ../src/graphics.cpp; sourceTree = ""; }; + 3BDB231B2564512500C4A63D /* sdl-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "sdl-util.h"; path = "../src/sdl-util.h"; sourceTree = ""; }; + 3BDB231C2564512500C4A63D /* aldatasource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = aldatasource.h; path = ../src/aldatasource.h; sourceTree = ""; }; + 3BDB231D2564512500C4A63D /* eventthread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = eventthread.cpp; path = ../src/eventthread.cpp; sourceTree = ""; }; + 3BDB231E2564512500C4A63D /* filesystem.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = filesystem.mm; path = ../src/filesystem.mm; sourceTree = ""; }; + 3BDB231F2564512500C4A63D /* gl-fun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "gl-fun.h"; path = "../src/gl-fun.h"; sourceTree = ""; }; + 3BDB23202564512500C4A63D /* rgssad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = rgssad.h; path = ../src/rgssad.h; sourceTree = ""; }; + 3BDB23212564512600C4A63D /* sharedstate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sharedstate.cpp; path = ../src/sharedstate.cpp; sourceTree = ""; }; + 3BDB23222564512600C4A63D /* lang-fun.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "lang-fun.mm"; path = "../src/lang-fun.mm"; sourceTree = ""; }; + 3BDB23232564512600C4A63D /* tileatlas.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tileatlas.cpp; path = ../src/tileatlas.cpp; sourceTree = ""; }; + 3BDB23242564512600C4A63D /* tilemap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tilemap.cpp; path = ../src/tilemap.cpp; sourceTree = ""; }; + 3BDB23252564512600C4A63D /* eventthread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = eventthread.h; path = ../src/eventthread.h; sourceTree = ""; }; + 3BDB23262564512600C4A63D /* font.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = font.cpp; path = ../src/font.cpp; sourceTree = ""; }; + 3BDB23272564512600C4A63D /* quad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = quad.h; path = ../src/quad.h; sourceTree = ""; }; + 3BDB23282564512600C4A63D /* debugwriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = debugwriter.h; path = ../src/debugwriter.h; sourceTree = ""; }; + 3BDB23292564512700C4A63D /* transform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = transform.h; path = ../src/transform.h; sourceTree = ""; }; + 3BDB232A2564512700C4A63D /* bitmap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bitmap.h; path = ../src/bitmap.h; sourceTree = ""; }; + 3BDB232B2564512700C4A63D /* etc-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "etc-internal.h"; path = "../src/etc-internal.h"; sourceTree = ""; }; + 3BDB232C2564512700C4A63D /* plane.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = plane.cpp; path = ../src/plane.cpp; sourceTree = ""; }; + 3BDB232D2564512700C4A63D /* viewport.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = viewport.cpp; path = ../src/viewport.cpp; sourceTree = ""; }; + 3BDB232E2564512700C4A63D /* keybindings.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = keybindings.cpp; path = ../src/keybindings.cpp; sourceTree = ""; }; + 3BDB232F2564512700C4A63D /* windowvx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = windowvx.h; path = ../src/windowvx.h; sourceTree = ""; }; + 3BDB23302564512700C4A63D /* input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = input.h; path = ../src/input.h; sourceTree = ""; }; + 3BDB23312564512800C4A63D /* vertex.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = vertex.h; path = ../src/vertex.h; sourceTree = ""; }; + 3BDB23322564512800C4A63D /* fake-api.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "fake-api.mm"; path = "../src/fake-api.mm"; sourceTree = ""; }; + 3BDB23332564512800C4A63D /* tilemap-common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "tilemap-common.h"; path = "../src/tilemap-common.h"; sourceTree = ""; }; + 3BDB23342564512800C4A63D /* scene.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = scene.h; path = ../src/scene.h; sourceTree = ""; }; + 3BDB23352564512800C4A63D /* font.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = font.h; path = ../src/font.h; sourceTree = ""; }; + 3BDB23362564512800C4A63D /* util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = util.h; path = ../src/util.h; sourceTree = ""; }; + 3BDB23372564512800C4A63D /* input.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = input.cpp; path = ../src/input.cpp; sourceTree = ""; }; + 3BDB23382564512800C4A63D /* midisource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = midisource.cpp; path = ../src/midisource.cpp; sourceTree = ""; }; + 3BDB23392564512900C4A63D /* texpool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = texpool.h; path = ../src/texpool.h; sourceTree = ""; }; + 3BDB233A2564512900C4A63D /* fake-api.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "fake-api.h"; path = "../src/fake-api.h"; sourceTree = ""; }; + 3BDB233B2564512900C4A63D /* sharedstate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sharedstate.h; path = ../src/sharedstate.h; sourceTree = ""; }; + 3BDB233C2564512900C4A63D /* binding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = binding.h; path = ../src/binding.h; sourceTree = ""; }; + 3BDB233D2564512900C4A63D /* sharedmidistate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sharedmidistate.h; path = ../src/sharedmidistate.h; sourceTree = ""; }; + 3BDB233E2564512900C4A63D /* window.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = window.h; path = ../src/window.h; sourceTree = ""; }; + 3BDB233F2564512900C4A63D /* bitmap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bitmap.cpp; path = ../src/bitmap.cpp; sourceTree = ""; }; + 3BDB23402564512900C4A63D /* gl-fun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gl-fun.cpp"; path = "../src/gl-fun.cpp"; sourceTree = ""; }; + 3BDB23412564512900C4A63D /* autotiles.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = autotiles.cpp; path = ../src/autotiles.cpp; sourceTree = ""; }; + 3BDB23422564512A00C4A63D /* flashable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = flashable.h; path = ../src/flashable.h; sourceTree = ""; }; + 3BDB23432564512A00C4A63D /* audiostream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = audiostream.cpp; path = ../src/audiostream.cpp; sourceTree = ""; }; + 3BDB23442564512A00C4A63D /* viewport.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = viewport.h; path = ../src/viewport.h; sourceTree = ""; }; + 3BDB23452564512A00C4A63D /* tileatlas.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tileatlas.h; path = ../src/tileatlas.h; sourceTree = ""; }; + 3BDB23462564512A00C4A63D /* al-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "al-util.h"; path = "../src/al-util.h"; sourceTree = ""; }; + 3BDB23472564512A00C4A63D /* serial-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "serial-util.h"; path = "../src/serial-util.h"; sourceTree = ""; }; + 3BDB23482564512B00C4A63D /* gl-meta.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gl-meta.cpp"; path = "../src/gl-meta.cpp"; sourceTree = ""; }; + 3BDB23492564512B00C4A63D /* keybindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = keybindings.h; path = ../src/keybindings.h; sourceTree = ""; }; + 3BDB234A2564512B00C4A63D /* rgssad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rgssad.cpp; path = ../src/rgssad.cpp; sourceTree = ""; }; + 3BDB234B2564512B00C4A63D /* alstream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = alstream.h; path = ../src/alstream.h; sourceTree = ""; }; + 3BDB234C2564512B00C4A63D /* texpool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = texpool.cpp; path = ../src/texpool.cpp; sourceTree = ""; }; + 3BDB234D2564512B00C4A63D /* tileatlasvx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tileatlasvx.h; path = ../src/tileatlasvx.h; sourceTree = ""; }; + 3BDB234E2564512B00C4A63D /* fluid-fun.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "fluid-fun.cpp"; path = "../src/fluid-fun.cpp"; sourceTree = ""; }; + 3BDB234F2564512B00C4A63D /* tilemap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = tilemap.h; path = ../src/tilemap.h; sourceTree = ""; }; + 3BDB23502564512B00C4A63D /* table.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = table.cpp; path = ../src/table.cpp; sourceTree = ""; }; + 3BDB23512564512B00C4A63D /* soundemitter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = soundemitter.h; path = ../src/soundemitter.h; sourceTree = ""; }; + 3BDB23522564512B00C4A63D /* windowvx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = windowvx.cpp; path = ../src/windowvx.cpp; sourceTree = ""; }; + 3BDB23532564512B00C4A63D /* audiostream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = audiostream.h; path = ../src/audiostream.h; sourceTree = ""; }; + 3BDB23542564512B00C4A63D /* scene.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = scene.cpp; path = ../src/scene.cpp; sourceTree = ""; }; + 3BDB23552564512B00C4A63D /* sdlsoundsource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = sdlsoundsource.cpp; path = ../src/sdlsoundsource.cpp; sourceTree = ""; }; + 3BDB23562564512C00C4A63D /* glstate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = glstate.h; path = ../src/glstate.h; sourceTree = ""; }; + 3BDB23572564512C00C4A63D /* tileatlasvx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tileatlasvx.cpp; path = ../src/tileatlasvx.cpp; sourceTree = ""; }; + 3BDB23582564512C00C4A63D /* shader.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = shader.cpp; path = ../src/shader.cpp; sourceTree = ""; }; + 3BDB235A2564512C00C4A63D /* audio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = audio.h; path = ../src/audio.h; sourceTree = ""; }; + 3BDB235B2564512C00C4A63D /* vorbissource.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = vorbissource.cpp; path = ../src/vorbissource.cpp; sourceTree = ""; }; + 3BDB235C2564512C00C4A63D /* lang-fun.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "lang-fun.h"; path = "../src/lang-fun.h"; sourceTree = ""; }; + 3BDB235D2564512C00C4A63D /* etc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = etc.h; path = ../src/etc.h; sourceTree = ""; }; + 3BDB235E2564512C00C4A63D /* alstream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = alstream.cpp; path = ../src/alstream.cpp; sourceTree = ""; }; + 3BDB235F2564512C00C4A63D /* intrulist.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = intrulist.h; path = ../src/intrulist.h; sourceTree = ""; }; + 3BDB23602564512D00C4A63D /* autotilesvx.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = autotilesvx.cpp; path = ../src/autotilesvx.cpp; sourceTree = ""; }; + 3BDB23612564512D00C4A63D /* gl-meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "gl-meta.h"; path = "../src/gl-meta.h"; sourceTree = ""; }; + 3BDB23622564512D00C4A63D /* settingsmenu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = settingsmenu.cpp; path = ../src/settingsmenu.cpp; sourceTree = ""; }; + 3BDB23632564512D00C4A63D /* global-ibo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "global-ibo.h"; path = "../src/global-ibo.h"; sourceTree = ""; }; + 3BDB23642564512D00C4A63D /* sprite.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sprite.h; path = ../src/sprite.h; sourceTree = ""; }; + 3BDB23652564512D00C4A63D /* plane.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = plane.h; path = ../src/plane.h; sourceTree = ""; }; + 3BDB23662564512D00C4A63D /* config.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = config.mm; path = ../src/config.mm; sourceTree = ""; }; + 3BDB23672564512D00C4A63D /* gl-debug.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gl-debug.cpp"; path = "../src/gl-debug.cpp"; sourceTree = ""; }; + 3BDB23682564512D00C4A63D /* tilequad.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = tilequad.cpp; path = ../src/tilequad.cpp; sourceTree = ""; }; + 3BDB23692564512D00C4A63D /* exception.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = exception.h; path = ../src/exception.h; sourceTree = ""; }; + 3BDB236A2564512D00C4A63D /* gl-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "gl-util.h"; path = "../src/gl-util.h"; sourceTree = ""; }; + 3BDB236B2564512D00C4A63D /* soundemitter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = soundemitter.cpp; path = ../src/soundemitter.cpp; sourceTree = ""; }; + 3BDB236C2564512D00C4A63D /* etc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = etc.cpp; path = ../src/etc.cpp; sourceTree = ""; }; + 3BDB236D2564512E00C4A63D /* config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = config.h; path = ../src/config.h; sourceTree = ""; }; + 3BDB236E2564512E00C4A63D /* table.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = table.h; path = ../src/table.h; sourceTree = ""; }; + 3BDB236F2564512E00C4A63D /* boost-hash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "boost-hash.h"; path = "../src/boost-hash.h"; sourceTree = ""; }; + 3BDB23702564512E00C4A63D /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = main.mm; path = ../src/main.mm; sourceTree = ""; }; + 3BDB23712564512E00C4A63D /* glstate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = glstate.cpp; path = ../src/glstate.cpp; sourceTree = ""; }; + 3BDB23722564512E00C4A63D /* window.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = window.cpp; path = ../src/window.cpp; sourceTree = ""; }; + 3BDB23A22564517100C4A63D /* table-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "table-binding.cpp"; path = "../binding/table-binding.cpp"; sourceTree = ""; }; + 3BDB23A32564517100C4A63D /* binding-types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "binding-types.h"; path = "../binding/binding-types.h"; sourceTree = ""; }; + 3BDB23A42564517100C4A63D /* windowvx-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "windowvx-binding.cpp"; path = "../binding/windowvx-binding.cpp"; sourceTree = ""; }; + 3BDB23A52564517100C4A63D /* tilemap-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "tilemap-binding.cpp"; path = "../binding/tilemap-binding.cpp"; sourceTree = ""; }; + 3BDB23A72564517100C4A63D /* sprite-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "sprite-binding.cpp"; path = "../binding/sprite-binding.cpp"; sourceTree = ""; }; + 3BDB23A82564517100C4A63D /* window-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "window-binding.cpp"; path = "../binding/window-binding.cpp"; sourceTree = ""; }; + 3BDB23A92564517100C4A63D /* input-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "input-binding.cpp"; path = "../binding/input-binding.cpp"; sourceTree = ""; }; + 3BDB23AA2564517100C4A63D /* etc-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "etc-binding.cpp"; path = "../binding/etc-binding.cpp"; sourceTree = ""; }; + 3BDB23AB2564517200C4A63D /* serializable-binding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "serializable-binding.h"; path = "../binding/serializable-binding.h"; sourceTree = ""; }; + 3BDB23AD2564517200C4A63D /* bitmap-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "bitmap-binding.cpp"; path = "../binding/bitmap-binding.cpp"; sourceTree = ""; }; + 3BDB23AF2564517200C4A63D /* font-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "font-binding.cpp"; path = "../binding/font-binding.cpp"; sourceTree = ""; }; + 3BDB23B02564517200C4A63D /* binding-mri.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "binding-mri.cpp"; path = "../binding/binding-mri.cpp"; sourceTree = ""; }; + 3BDB23B22564517300C4A63D /* graphics-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "graphics-binding.cpp"; path = "../binding/graphics-binding.cpp"; sourceTree = ""; }; + 3BDB23B32564517300C4A63D /* miniffi-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "miniffi-binding.cpp"; path = "../binding/miniffi-binding.cpp"; sourceTree = ""; }; + 3BDB23B42564517300C4A63D /* viewport-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "viewport-binding.cpp"; path = "../binding/viewport-binding.cpp"; sourceTree = ""; }; + 3BDB23B52564517300C4A63D /* binding-util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "binding-util.h"; path = "../binding/binding-util.h"; sourceTree = ""; }; + 3BDB23B62564517300C4A63D /* flashable-binding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "flashable-binding.h"; path = "../binding/flashable-binding.h"; sourceTree = ""; }; + 3BDB23B72564517300C4A63D /* filesystem-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "filesystem-binding.cpp"; path = "../binding/filesystem-binding.cpp"; sourceTree = ""; }; + 3BDB23B82564517300C4A63D /* tilemapvx-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "tilemapvx-binding.cpp"; path = "../binding/tilemapvx-binding.cpp"; sourceTree = ""; }; + 3BDB23B92564517400C4A63D /* disposable-binding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "disposable-binding.h"; path = "../binding/disposable-binding.h"; sourceTree = ""; }; + 3BDB23BA2564517400C4A63D /* audio-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "audio-binding.cpp"; path = "../binding/audio-binding.cpp"; sourceTree = ""; }; + 3BDB23BB2564517400C4A63D /* viewportelement-binding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "viewportelement-binding.h"; path = "../binding/viewportelement-binding.h"; sourceTree = ""; }; + 3BDB23BC2564517400C4A63D /* cusl-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "cusl-binding.cpp"; path = "../binding/cusl-binding.cpp"; sourceTree = ""; }; + 3BDB23BD2564517400C4A63D /* sceneelement-binding.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "sceneelement-binding.h"; path = "../binding/sceneelement-binding.h"; sourceTree = ""; }; + 3BDB23BE2564517400C4A63D /* module_rpg.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = module_rpg.cpp; path = ../binding/module_rpg.cpp; sourceTree = ""; }; + 3BDB23BF2564517400C4A63D /* plane-binding.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "plane-binding.cpp"; path = "../binding/plane-binding.cpp"; sourceTree = ""; }; + 3BDB23C02564517500C4A63D /* binding-util.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "binding-util.cpp"; path = "../binding/binding-util.cpp"; sourceTree = ""; }; + 3BDB23E22564546E00C4A63D /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon.icns; sourceTree = ""; }; + 3BDB240A2564715C00C4A63D /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; + 3BDB25B02565175000C4A63D /* mkxp-z.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "mkxp-z.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3BDB25C52565184600C4A63D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 3BA08EA1256641ED00449CFF /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BD2B6E12565AEC0003DAD8A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BD2B6E72565AEC0003DAD8A /* IOKit.framework in Frameworks */, + 3BA08E5825661C4700449CFF /* Vorbis.framework in Frameworks */, + 3BA08E5C25661C4700449CFF /* MetalANGLE.framework in Frameworks */, + 3BA08E6025661C4800449CFF /* SDL2.framework in Frameworks */, + 3BD2B6ED2565AEC0003DAD8A /* libiconv.tbd in Frameworks */, + 3BD2B6EE2565AEC0003DAD8A /* OpenAL.framework in Frameworks */, + 3BA08E5E25661C4800449CFF /* Ogg.framework in Frameworks */, + 3BA08E5A25661C4700449CFF /* SDL2_ttf.framework in Frameworks */, + 3BA08E6225661C4800449CFF /* SDL2_image.framework in Frameworks */, + 3BD2B6EF2565AEC0003DAD8A /* CoreFoundation.framework in Frameworks */, + 3BD2B6F12565AEC0003DAD8A /* Foundation.framework in Frameworks */, + 3BA08E9B256638C900449CFF /* AudioToolbox.framework in Frameworks */, + 3BA08E6525661CA600449CFF /* libfluidsynth.3.dylib in Frameworks */, + 3BD2B6F32565AEC0003DAD8A /* libz.tbd in Frameworks */, + 3BA08E7625661CDA00449CFF /* libsigc-2.0.a in Frameworks */, + 3BA08E7425661CDA00449CFF /* libobjfw.a in Frameworks */, + 3BA08E7225661CDA00449CFF /* libobjfwbridge.a in Frameworks */, + 3BA08E7025661CDA00449CFF /* libphysfs.a in Frameworks */, + 3BA08E7E25661D3700449CFF /* libruby.2.7.dylib in Frameworks */, + 3BA08E7825661CDA00449CFF /* libpixman-1.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BDB25AD2565175000C4A63D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BD2B47B256534C0003DAD8A /* IOKit.framework in Frameworks */, + 3BA08E8A25661E3500449CFF /* Vorbis.framework in Frameworks */, + 3BA08E6625661CA600449CFF /* libfluidsynth.3.dylib in Frameworks */, + 3BD2B46D25651DE8003DAD8A /* libiconv.tbd in Frameworks */, + 3BA08E7925661CDA00449CFF /* libpixman-1.a in Frameworks */, + 3BA08E8625661E3500449CFF /* SDL2_ttf.framework in Frameworks */, + 3BD2B46B25651DE2003DAD8A /* OpenAL.framework in Frameworks */, + 3BD2B46E25651DF8003DAD8A /* CoreFoundation.framework in Frameworks */, + 3BA08E8825661E3500449CFF /* SDL2.framework in Frameworks */, + 3BA08E8425661E3400449CFF /* SDL2_image.framework in Frameworks */, + 3BA08E8225661E3400449CFF /* Ogg.framework in Frameworks */, + 3BA08E9A256638C600449CFF /* AudioToolbox.framework in Frameworks */, + 3BD2B46F25651DFA003DAD8A /* Foundation.framework in Frameworks */, + 3BA08E8025661E3400449CFF /* MetalANGLE.framework in Frameworks */, + 3BD2B46C25651DE6003DAD8A /* libz.tbd in Frameworks */, + 3BA08E7525661CDA00449CFF /* libobjfw.a in Frameworks */, + 3BA08E7325661CDA00449CFF /* libobjfwbridge.a in Frameworks */, + 3BA08E7C25661D2C00449CFF /* libruby.1.8.7.dylib in Frameworks */, + 3BA08E7725661CDA00449CFF /* libsigc-2.0.a in Frameworks */, + 3BA08E7125661CDA00449CFF /* libphysfs.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3BA08E7A25661D0E00449CFF /* Ruby */ = { + isa = PBXGroup; + children = ( + 3BA08E7D25661D3700449CFF /* libruby.2.7.dylib */, + 3BA08E7B25661D2C00449CFF /* libruby.1.8.7.dylib */, + ); + name = Ruby; + sourceTree = ""; + }; + 3BA08EA5256641ED00449CFF /* misc */ = { + isa = PBXGroup; + children = ( + 3BA08EA6256641EE00449CFF /* Assets.plist */, + ); + path = misc; + sourceTree = ""; + }; + 3BA08EE625664BCC00449CFF /* SDL2_sound */ = { + isa = PBXGroup; + children = ( + 3BA08EE725664BCC00449CFF /* SDL_sound_shn.c */, + 3BA08EE825664BCC00449CFF /* SDL_sound.h */, + 3BA08EE925664BCC00449CFF /* SDL_sound_coreaudio.c */, + 3BA08EEA25664BCC00449CFF /* SDL_sound_skeleton.c */, + 3BA08EEB25664BCC00449CFF /* SDL_sound_internal.h */, + 3BA08EEC25664BCC00449CFF /* SDL_sound_raw.c */, + 3BA08EED25664BCC00449CFF /* SDL_sound_modplug.c */, + 3BA08EEE25664BCC00449CFF /* dr_mp3.h */, + 3BA08EEF25664BCC00449CFF /* SDL_sound_vorbis.c */, + 3BA08EF025664BCC00449CFF /* SDL_sound.c */, + 3BA08EF125664BCC00449CFF /* SDL_sound_voc.c */, + 3BA08EF225664BCC00449CFF /* dr_flac.h */, + 3BA08EF325664BCC00449CFF /* SDL_sound_wav.c */, + 3BA08EF425664BCC00449CFF /* SDL_sound_au.c */, + 3BA08EF525664BCC00449CFF /* SDL_sound_mp3.c */, + 3BA08EF625664BCC00449CFF /* SDL_sound_aiff.c */, + 3BA08EF725664BCC00449CFF /* SDL_sound_flac.c */, + 3BA08EF825664BCC00449CFF /* stb_vorbis.h */, + ); + name = SDL2_sound; + path = ../SDL2_sound; + sourceTree = ""; + }; + 3BD2B3B2256518A1003DAD8A /* Configuration */ = { + isa = PBXGroup; + children = ( + 3BD2B7282565B35D003DAD8A /* mkxp.json */, + 3BDB240A2564715C00C4A63D /* Config.xcconfig */, + 3BD2B3AF25651885003DAD8A /* entitlements.plist */, + 3BDB25C52565184600C4A63D /* Info.plist */, + ); + name = Configuration; + sourceTree = ""; + }; + 3BD2B46425651BEB003DAD8A /* System */ = { + isa = PBXGroup; + children = ( + 3BD2B47A256534BA003DAD8A /* IOKit.framework */, + 3BD2B46A25651C2D003DAD8A /* OpenAL.framework */, + 3BD2B46925651C1B003DAD8A /* AudioToolbox.framework */, + 3BD2B46725651C00003DAD8A /* Foundation.framework */, + 3BD2B46625651BFC003DAD8A /* CoreFoundation.framework */, + 3BD2B46525651BF3003DAD8A /* libz.tbd */, + 3BD2B46825651C12003DAD8A /* libiconv.tbd */, + ); + name = System; + sourceTree = ""; + }; + 3BD2B4AB25654A49003DAD8A /* Shaders */ = { + isa = PBXGroup; + children = ( + 3BD2B4BB25654AD7003DAD8A /* bitmapBlit.frag */, + 3BD2B4AC25654AD6003DAD8A /* blur.frag */, + 3BD2B4B825654AD7003DAD8A /* blurH.vert */, + 3BD2B4B025654AD6003DAD8A /* blurV.vert */, + 3BD2B4BC25654AD7003DAD8A /* common.h */, + 3BD2B4C425654AD8003DAD8A /* flashMap.frag */, + 3BD2B4B725654AD7003DAD8A /* flatColor.frag */, + 3BD2B4AE25654AD6003DAD8A /* gray.frag */, + 3BD2B4C325654AD8003DAD8A /* hue.frag */, + 3BD2B4B325654AD6003DAD8A /* minimal.vert */, + 3BD2B4B625654AD6003DAD8A /* plane.frag */, + 3BD2B4AF25654AD6003DAD8A /* simple.frag */, + 3BD2B4B525654AD6003DAD8A /* simple.vert */, + 3BD2B4BD25654AD7003DAD8A /* simpleAlpha.frag */, + 3BD2B4B925654AD7003DAD8A /* simpleAlphaUni.frag */, + 3BD2B4BF25654AD8003DAD8A /* simpleColor.frag */, + 3BD2B4BE25654AD8003DAD8A /* simpleColor.vert */, + 3BD2B4AD25654AD6003DAD8A /* simpleMatrix.vert */, + 3BD2B4C225654AD8003DAD8A /* sprite.frag */, + 3BD2B4B125654AD6003DAD8A /* sprite.vert */, + 3BD2B4C125654AD8003DAD8A /* tilemap.frag */, + 3BD2B4B225654AD6003DAD8A /* tilemap.vert */, + 3BD2B4C025654AD8003DAD8A /* tilemapvx.vert */, + 3BD2B4B425654AD6003DAD8A /* trans.frag */, + 3BD2B4BA25654AD7003DAD8A /* transSimple.frag */, + ); + name = Shaders; + sourceTree = ""; + }; + 3BD2B4C525654B4C003DAD8A /* Scripts */ = { + isa = PBXGroup; + children = ( + 3BD2B4C625654B6F003DAD8A /* EssentialsCompatibility.rb */, + ); + name = Scripts; + sourceTree = ""; + }; + 3BD2B4F625655886003DAD8A /* Cocoa */ = { + isa = PBXGroup; + children = ( + 3BD2B4F7256558B6003DAD8A /* CocoaHelpers.mm */, + 3BD2B4F8256558B6003DAD8A /* CocoaHelpers.hpp */, + ); + name = Cocoa; + sourceTree = ""; + }; + 3BDB22EB25644FBF00C4A63D = { + isa = PBXGroup; + children = ( + 3BD2B7272565B343003DAD8A /* README.md */, + 3BDB2409256470AE00C4A63D /* Player */, + 3BA08EA5256641ED00449CFF /* misc */, + 3BDB22F72564501400C4A63D /* Products */, + 3BDB23E5256455A400C4A63D /* Frameworks */, + ); + sourceTree = ""; + }; + 3BDB22F72564501400C4A63D /* Products */ = { + isa = PBXGroup; + children = ( + 3BDB25B02565175000C4A63D /* mkxp-z.app */, + 3BD2B7252565AEC0003DAD8A /* mkxp-z.app */, + 3BA08EA4256641ED00449CFF /* Assets.bundle */, + ); + name = Products; + sourceTree = ""; + }; + 3BDB230A256450EE00C4A63D /* Core Source */ = { + isa = PBXGroup; + children = ( + 3BD2B4F625655886003DAD8A /* Cocoa */, + 3BD2B4AB25654A49003DAD8A /* Shaders */, + 3BDB23462564512A00C4A63D /* al-util.h */, + 3BDB231C2564512500C4A63D /* aldatasource.h */, + 3BDB235E2564512C00C4A63D /* alstream.cpp */, + 3BDB234B2564512B00C4A63D /* alstream.h */, + 3BDB23152564512400C4A63D /* audio.cpp */, + 3BDB235A2564512C00C4A63D /* audio.h */, + 3BDB23432564512A00C4A63D /* audiostream.cpp */, + 3BDB23532564512B00C4A63D /* audiostream.h */, + 3BDB23412564512900C4A63D /* autotiles.cpp */, + 3BDB23602564512D00C4A63D /* autotilesvx.cpp */, + 3BDB233C2564512900C4A63D /* binding.h */, + 3BDB233F2564512900C4A63D /* bitmap.cpp */, + 3BDB232A2564512700C4A63D /* bitmap.h */, + 3BDB236F2564512E00C4A63D /* boost-hash.h */, + 3BDB236D2564512E00C4A63D /* config.h */, + 3BDB23662564512D00C4A63D /* config.mm */, + 3BDB23282564512600C4A63D /* debugwriter.h */, + 3BDB23102564512400C4A63D /* disposable.h */, + 3BDB232B2564512700C4A63D /* etc-internal.h */, + 3BDB236C2564512D00C4A63D /* etc.cpp */, + 3BDB235D2564512C00C4A63D /* etc.h */, + 3BDB231D2564512500C4A63D /* eventthread.cpp */, + 3BDB23252564512600C4A63D /* eventthread.h */, + 3BDB23692564512D00C4A63D /* exception.h */, + 3BDB233A2564512900C4A63D /* fake-api.h */, + 3BDB23322564512800C4A63D /* fake-api.mm */, + 3BDB230C2564512300C4A63D /* filesystem.h */, + 3BDB231E2564512500C4A63D /* filesystem.mm */, + 3BDB23422564512A00C4A63D /* flashable.h */, + 3BDB234E2564512B00C4A63D /* fluid-fun.cpp */, + 3BDB230F2564512400C4A63D /* fluid-fun.h */, + 3BDB23262564512600C4A63D /* font.cpp */, + 3BDB23352564512800C4A63D /* font.h */, + 3BDB23672564512D00C4A63D /* gl-debug.cpp */, + 3BDB23132564512400C4A63D /* gl-debug.h */, + 3BDB23402564512900C4A63D /* gl-fun.cpp */, + 3BDB231F2564512500C4A63D /* gl-fun.h */, + 3BDB23482564512B00C4A63D /* gl-meta.cpp */, + 3BDB23612564512D00C4A63D /* gl-meta.h */, + 3BDB236A2564512D00C4A63D /* gl-util.h */, + 3BDB23632564512D00C4A63D /* global-ibo.h */, + 3BDB23712564512E00C4A63D /* glstate.cpp */, + 3BDB23562564512C00C4A63D /* glstate.h */, + 3BDB231A2564512500C4A63D /* graphics.cpp */, + 3BDB23192564512500C4A63D /* graphics.h */, + 3BDB23372564512800C4A63D /* input.cpp */, + 3BDB23302564512700C4A63D /* input.h */, + 3BDB235F2564512C00C4A63D /* intrulist.h */, + 3BDB232E2564512700C4A63D /* keybindings.cpp */, + 3BDB23492564512B00C4A63D /* keybindings.h */, + 3BDB235C2564512C00C4A63D /* lang-fun.h */, + 3BDB23222564512600C4A63D /* lang-fun.mm */, + 3BDB23702564512E00C4A63D /* main.mm */, + 3BDB23382564512800C4A63D /* midisource.cpp */, + 3BDB232C2564512700C4A63D /* plane.cpp */, + 3BDB23652564512D00C4A63D /* plane.h */, + 3BDB23272564512600C4A63D /* quad.h */, + 3BDB230B2564512300C4A63D /* quadarray.h */, + 3BDB234A2564512B00C4A63D /* rgssad.cpp */, + 3BDB23202564512500C4A63D /* rgssad.h */, + 3BDB23542564512B00C4A63D /* scene.cpp */, + 3BDB23342564512800C4A63D /* scene.h */, + 3BDB231B2564512500C4A63D /* sdl-util.h */, + 3BDB23552564512B00C4A63D /* sdlsoundsource.cpp */, + 3BDB23472564512A00C4A63D /* serial-util.h */, + 3BDB230D2564512300C4A63D /* serializable.h */, + 3BDB23622564512D00C4A63D /* settingsmenu.cpp */, + 3BDB23182564512400C4A63D /* settingsmenu.h */, + 3BDB23582564512C00C4A63D /* shader.cpp */, + 3BDB23122564512400C4A63D /* shader.h */, + 3BDB233D2564512900C4A63D /* sharedmidistate.h */, + 3BDB23212564512600C4A63D /* sharedstate.cpp */, + 3BDB233B2564512900C4A63D /* sharedstate.h */, + 3BDB236B2564512D00C4A63D /* soundemitter.cpp */, + 3BDB23512564512B00C4A63D /* soundemitter.h */, + 3BDB23142564512400C4A63D /* sprite.cpp */, + 3BDB23642564512D00C4A63D /* sprite.h */, + 3BDB23502564512B00C4A63D /* table.cpp */, + 3BDB236E2564512E00C4A63D /* table.h */, + 3BDB234C2564512B00C4A63D /* texpool.cpp */, + 3BDB23392564512900C4A63D /* texpool.h */, + 3BDB23232564512600C4A63D /* tileatlas.cpp */, + 3BDB23452564512A00C4A63D /* tileatlas.h */, + 3BDB23572564512C00C4A63D /* tileatlasvx.cpp */, + 3BDB234D2564512B00C4A63D /* tileatlasvx.h */, + 3BDB23332564512800C4A63D /* tilemap-common.h */, + 3BDB23242564512600C4A63D /* tilemap.cpp */, + 3BDB234F2564512B00C4A63D /* tilemap.h */, + 3BDB23162564512400C4A63D /* tilemapvx.cpp */, + 3BDB230E2564512300C4A63D /* tilemapvx.h */, + 3BDB23682564512D00C4A63D /* tilequad.cpp */, + 3BDB23172564512400C4A63D /* tilequad.h */, + 3BDB23292564512700C4A63D /* transform.h */, + 3BDB23362564512800C4A63D /* util.h */, + 3BDB23112564512400C4A63D /* vertex.cpp */, + 3BDB23312564512800C4A63D /* vertex.h */, + 3BDB232D2564512700C4A63D /* viewport.cpp */, + 3BDB23442564512A00C4A63D /* viewport.h */, + 3BDB235B2564512C00C4A63D /* vorbissource.cpp */, + 3BDB23722564512E00C4A63D /* window.cpp */, + 3BDB233E2564512900C4A63D /* window.h */, + 3BDB23522564512B00C4A63D /* windowvx.cpp */, + 3BDB232F2564512700C4A63D /* windowvx.h */, + ); + name = "Core Source"; + sourceTree = ""; + }; + 3BDB23A12564515900C4A63D /* Binding Source */ = { + isa = PBXGroup; + children = ( + 3BD2B4C525654B4C003DAD8A /* Scripts */, + 3BDB23BA2564517400C4A63D /* audio-binding.cpp */, + 3BDB23B02564517200C4A63D /* binding-mri.cpp */, + 3BDB23A32564517100C4A63D /* binding-types.h */, + 3BDB23C02564517500C4A63D /* binding-util.cpp */, + 3BDB23B52564517300C4A63D /* binding-util.h */, + 3BDB23AD2564517200C4A63D /* bitmap-binding.cpp */, + 3BDB23BC2564517400C4A63D /* cusl-binding.cpp */, + 3BDB23B92564517400C4A63D /* disposable-binding.h */, + 3BDB23AA2564517100C4A63D /* etc-binding.cpp */, + 3BDB23B72564517300C4A63D /* filesystem-binding.cpp */, + 3BDB23B62564517300C4A63D /* flashable-binding.h */, + 3BDB23AF2564517200C4A63D /* font-binding.cpp */, + 3BDB23B22564517300C4A63D /* graphics-binding.cpp */, + 3BDB23A92564517100C4A63D /* input-binding.cpp */, + 3BDB23B32564517300C4A63D /* miniffi-binding.cpp */, + 3BDB23BE2564517400C4A63D /* module_rpg.cpp */, + 3BDB23BF2564517400C4A63D /* plane-binding.cpp */, + 3BDB23BD2564517400C4A63D /* sceneelement-binding.h */, + 3BDB23AB2564517200C4A63D /* serializable-binding.h */, + 3BDB23A72564517100C4A63D /* sprite-binding.cpp */, + 3BDB23A22564517100C4A63D /* table-binding.cpp */, + 3BDB23A52564517100C4A63D /* tilemap-binding.cpp */, + 3BDB23B82564517300C4A63D /* tilemapvx-binding.cpp */, + 3BDB23B42564517300C4A63D /* viewport-binding.cpp */, + 3BDB23BB2564517400C4A63D /* viewportelement-binding.h */, + 3BDB23A82564517100C4A63D /* window-binding.cpp */, + 3BDB23A42564517100C4A63D /* windowvx-binding.cpp */, + ); + name = "Binding Source"; + sourceTree = ""; + }; + 3BDB23E12564545F00C4A63D /* Assets */ = { + isa = PBXGroup; + children = ( + 3BD2B4F125654DE3003DAD8A /* wqymicrohei.ttf */, + 3BD2B4F025654DDB003DAD8A /* liberation.ttf */, + 3BD2B4EE25654DC7003DAD8A /* icon.png */, + 3BDB23E22564546E00C4A63D /* icon.icns */, + ); + name = Assets; + sourceTree = ""; + }; + 3BDB23E5256455A400C4A63D /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3BA08E7A25661D0E00449CFF /* Ruby */, + 3BD2B46425651BEB003DAD8A /* System */, + 3BA08E5425661C4700449CFF /* MetalANGLE.framework */, + 3BA08E5525661C4700449CFF /* Ogg.framework */, + 3BA08E5725661C4700449CFF /* SDL2_image.framework */, + 3BA08E5325661C4700449CFF /* SDL2_ttf.framework */, + 3BA08E5625661C4700449CFF /* SDL2.framework */, + 3BA08E5225661C4600449CFF /* Vorbis.framework */, + 3BA08E6425661CA600449CFF /* libfluidsynth.3.dylib */, + 3BA08E6B25661CD900449CFF /* libobjfw.a */, + 3BA08E6A25661CD900449CFF /* libobjfwbridge.a */, + 3BA08E6925661CD900449CFF /* libphysfs.a */, + 3BA08E6D25661CD900449CFF /* libpixman-1.a */, + 3BA08E6C25661CD900449CFF /* libsigc-2.0.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 3BDB2409256470AE00C4A63D /* Player */ = { + isa = PBXGroup; + children = ( + 3BD2B3B2256518A1003DAD8A /* Configuration */, + 3BDB230A256450EE00C4A63D /* Core Source */, + 3BDB23A12564515900C4A63D /* Binding Source */, + 3BDB23E12564545F00C4A63D /* Assets */, + 3BA08EE625664BCC00449CFF /* SDL2_sound */, + ); + name = Player; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXLegacyTarget section */ + 3BA08E902566314300449CFF /* Obtain Dependencies */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "-f Deps.make $(ACTION)"; + buildConfigurationList = 3BA08E912566314300449CFF /* Build configuration list for PBXLegacyTarget "Obtain Dependencies" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = ""; + dependencies = ( + ); + name = "Obtain Dependencies"; + passBuildSettingsInEnvironment = 1; + productName = gen; + }; + 3BA08E9C25663C2E00449CFF /* Remove Dependencies */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "clean -f Deps.make"; + buildConfigurationList = 3BA08E9D25663C2E00449CFF /* Build configuration list for PBXLegacyTarget "Remove Dependencies" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = ""; + dependencies = ( + ); + name = "Remove Dependencies"; + passBuildSettingsInEnvironment = 1; + productName = "Remove Dependencies"; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXNativeTarget section */ + 3BA08EA3256641ED00449CFF /* Assets */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3BA08EA9256641EE00449CFF /* Build configuration list for PBXNativeTarget "Assets" */; + buildPhases = ( + 3BA08EA0256641ED00449CFF /* Sources */, + 3BA08EA1256641ED00449CFF /* Frameworks */, + 3BA08EA2256641ED00449CFF /* Resources */, + 3BA08EAC2566426200449CFF /* CopyFiles */, + 3BA08EC8256642C900449CFF /* CopyFiles */, + 3BA08EE12566499400449CFF /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Assets; + productName = "mkxpz-resources"; + productReference = 3BA08EA4256641ED00449CFF /* Assets.bundle */; + productType = "com.apple.product-type.bundle"; + }; + 3BD2B64B2565AEC0003DAD8A /* Player */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3BD2B7222565AEC0003DAD8A /* Build configuration list for PBXNativeTarget "Player" */; + buildPhases = ( + 3BD2B64C2565AEC0003DAD8A /* Sources */, + 3BD2B6E12565AEC0003DAD8A /* Frameworks */, + 3BD2B6F82565AEC0003DAD8A /* Resources */, + 3BD2B6FA2565AEC0003DAD8A /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 3BA08ED3256643AD00449CFF /* PBXTargetDependency */, + 3BA08E95256631FD00449CFF /* PBXTargetDependency */, + ); + name = Player; + productName = PlayerLegacy; + productReference = 3BD2B7252565AEC0003DAD8A /* mkxp-z.app */; + productType = "com.apple.product-type.application"; + }; + 3BDB25AF2565175000C4A63D /* PlayerLegacy */ = { + isa = PBXNativeTarget; + buildConfigurationList = 3BDB25BE2565175400C4A63D /* Build configuration list for PBXNativeTarget "PlayerLegacy" */; + buildPhases = ( + 3BDB25AC2565175000C4A63D /* Sources */, + 3BDB25AD2565175000C4A63D /* Frameworks */, + 3BDB25AE2565175000C4A63D /* Resources */, + 3BD2B3B8256518DD003DAD8A /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 3BA08ED1256643A600449CFF /* PBXTargetDependency */, + 3BA08E972566320100449CFF /* PBXTargetDependency */, + ); + name = PlayerLegacy; + productName = PlayerLegacy; + productReference = 3BDB25B02565175000C4A63D /* mkxp-z.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3BDB22EC25644FBF00C4A63D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1220; + TargetAttributes = { + 3BA08E902566314300449CFF = { + CreatedOnToolsVersion = 12.2; + }; + 3BA08E9C25663C2E00449CFF = { + CreatedOnToolsVersion = 12.2; + }; + 3BA08EA3256641ED00449CFF = { + CreatedOnToolsVersion = 12.2; + }; + 3BDB25AF2565175000C4A63D = { + CreatedOnToolsVersion = 12.2; + }; + }; + }; + buildConfigurationList = 3BDB22EF25644FBF00C4A63D /* Build configuration list for PBXProject "mkxp-z" */; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 3BDB22EB25644FBF00C4A63D; + productRefGroup = 3BDB22F72564501400C4A63D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 3BA08EA3256641ED00449CFF /* Assets */, + 3BA08E902566314300449CFF /* Obtain Dependencies */, + 3BD2B64B2565AEC0003DAD8A /* Player */, + 3BDB25AF2565175000C4A63D /* PlayerLegacy */, + 3BA08E9C25663C2E00449CFF /* Remove Dependencies */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 3BA08EA2256641ED00449CFF /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BA08ECE2566437500449CFF /* icon.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BD2B6F82565AEC0003DAD8A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BA08ED4256643C200449CFF /* Assets.bundle in Resources */, + 3BD2B6F92565AEC0003DAD8A /* icon.icns in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BDB25AE2565175000C4A63D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BA08ED5256643E300449CFF /* Assets.bundle in Resources */, + 3BD2B46325651B73003DAD8A /* icon.icns in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3BA08EA0256641ED00449CFF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BD2B64C2565AEC0003DAD8A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BD2B64D2565AEC0003DAD8A /* audio-binding.cpp in Sources */, + 3BA08F1125664BCD00449CFF /* SDL_sound_flac.c in Sources */, + 3BD2B64E2565AEC0003DAD8A /* binding-mri.cpp in Sources */, + 3BD2B64F2565AEC0003DAD8A /* binding-types.h in Sources */, + 3BA08EF925664BCC00449CFF /* SDL_sound_shn.c in Sources */, + 3BD2B6502565AEC0003DAD8A /* binding-util.cpp in Sources */, + 3BD2B6512565AEC0003DAD8A /* binding-util.h in Sources */, + 3BD2B6522565AEC0003DAD8A /* bitmap-binding.cpp in Sources */, + 3BD2B6532565AEC0003DAD8A /* cusl-binding.cpp in Sources */, + 3BD2B6542565AEC0003DAD8A /* disposable-binding.h in Sources */, + 3BD2B6552565AEC0003DAD8A /* etc-binding.cpp in Sources */, + 3BD2B6562565AEC0003DAD8A /* filesystem-binding.cpp in Sources */, + 3BD2B6572565AEC0003DAD8A /* flashable-binding.h in Sources */, + 3BD2B6582565AEC0003DAD8A /* font-binding.cpp in Sources */, + 3BD2B6592565AEC0003DAD8A /* graphics-binding.cpp in Sources */, + 3BD2B65A2565AEC0003DAD8A /* input-binding.cpp in Sources */, + 3BD2B65B2565AEC0003DAD8A /* miniffi-binding.cpp in Sources */, + 3BD2B65C2565AEC0003DAD8A /* module_rpg.cpp in Sources */, + 3BD2B65D2565AEC0003DAD8A /* plane-binding.cpp in Sources */, + 3BD2B65E2565AEC0003DAD8A /* sceneelement-binding.h in Sources */, + 3BD2B65F2565AEC0003DAD8A /* serializable-binding.h in Sources */, + 3BD2B6602565AEC0003DAD8A /* sprite-binding.cpp in Sources */, + 3BD2B6612565AEC0003DAD8A /* table-binding.cpp in Sources */, + 3BD2B6622565AEC0003DAD8A /* tilemap-binding.cpp in Sources */, + 3BD2B6632565AEC0003DAD8A /* tilemapvx-binding.cpp in Sources */, + 3BD2B6642565AEC0003DAD8A /* viewport-binding.cpp in Sources */, + 3BD2B6652565AEC0003DAD8A /* viewportelement-binding.h in Sources */, + 3BD2B6662565AEC0003DAD8A /* window-binding.cpp in Sources */, + 3BD2B6672565AEC0003DAD8A /* windowvx-binding.cpp in Sources */, + 3BD2B6682565AEC0003DAD8A /* al-util.h in Sources */, + 3BD2B6692565AEC0003DAD8A /* aldatasource.h in Sources */, + 3BD2B66A2565AEC0003DAD8A /* alstream.cpp in Sources */, + 3BD2B66B2565AEC0003DAD8A /* alstream.h in Sources */, + 3BD2B66C2565AEC0003DAD8A /* audio.cpp in Sources */, + 3BD2B66D2565AEC0003DAD8A /* audio.h in Sources */, + 3BD2B66E2565AEC0003DAD8A /* audiostream.cpp in Sources */, + 3BD2B66F2565AEC0003DAD8A /* audiostream.h in Sources */, + 3BD2B6702565AEC0003DAD8A /* autotiles.cpp in Sources */, + 3BD2B6712565AEC0003DAD8A /* autotilesvx.cpp in Sources */, + 3BD2B6722565AEC0003DAD8A /* binding.h in Sources */, + 3BD2B6732565AEC0003DAD8A /* bitmap.cpp in Sources */, + 3BD2B6742565AEC0003DAD8A /* bitmap.h in Sources */, + 3BD2B6752565AEC0003DAD8A /* boost-hash.h in Sources */, + 3BA08F0B25664BCD00449CFF /* SDL_sound_au.c in Sources */, + 3BD2B6762565AEC0003DAD8A /* config.h in Sources */, + 3BD2B6772565AEC0003DAD8A /* config.mm in Sources */, + 3BD2B6782565AEC0003DAD8A /* debugwriter.h in Sources */, + 3BD2B6792565AEC0003DAD8A /* disposable.h in Sources */, + 3BD2B67A2565AEC0003DAD8A /* CocoaHelpers.mm in Sources */, + 3BA08F0725664BCD00449CFF /* SDL_sound_voc.c in Sources */, + 3BD2B67B2565AEC0003DAD8A /* etc-internal.h in Sources */, + 3BD2B67C2565AEC0003DAD8A /* etc.cpp in Sources */, + 3BD2B67D2565AEC0003DAD8A /* etc.h in Sources */, + 3BD2B67E2565AEC0003DAD8A /* eventthread.cpp in Sources */, + 3BD2B67F2565AEC0003DAD8A /* eventthread.h in Sources */, + 3BD2B6802565AEC0003DAD8A /* exception.h in Sources */, + 3BD2B6812565AEC0003DAD8A /* fake-api.h in Sources */, + 3BD2B6822565AEC0003DAD8A /* fake-api.mm in Sources */, + 3BD2B6832565AEC0003DAD8A /* filesystem.h in Sources */, + 3BD2B6842565AEC0003DAD8A /* filesystem.mm in Sources */, + 3BD2B6852565AEC0003DAD8A /* flashable.h in Sources */, + 3BD2B6862565AEC0003DAD8A /* fluid-fun.cpp in Sources */, + 3BD2B6872565AEC0003DAD8A /* fluid-fun.h in Sources */, + 3BD2B6882565AEC0003DAD8A /* font.cpp in Sources */, + 3BD2B6892565AEC0003DAD8A /* font.h in Sources */, + 3BD2B68A2565AEC0003DAD8A /* gl-debug.cpp in Sources */, + 3BD2B68B2565AEC0003DAD8A /* gl-debug.h in Sources */, + 3BD2B68C2565AEC0003DAD8A /* gl-fun.cpp in Sources */, + 3BD2B68D2565AEC0003DAD8A /* gl-fun.h in Sources */, + 3BD2B68E2565AEC0003DAD8A /* gl-meta.cpp in Sources */, + 3BD2B68F2565AEC0003DAD8A /* gl-meta.h in Sources */, + 3BD2B6902565AEC0003DAD8A /* gl-util.h in Sources */, + 3BD2B6912565AEC0003DAD8A /* global-ibo.h in Sources */, + 3BD2B6922565AEC0003DAD8A /* glstate.cpp in Sources */, + 3BD2B6932565AEC0003DAD8A /* glstate.h in Sources */, + 3BD2B6942565AEC0003DAD8A /* graphics.cpp in Sources */, + 3BD2B6952565AEC0003DAD8A /* graphics.h in Sources */, + 3BA08F0D25664BCD00449CFF /* SDL_sound_mp3.c in Sources */, + 3BD2B6962565AEC0003DAD8A /* input.cpp in Sources */, + 3BD2B6972565AEC0003DAD8A /* input.h in Sources */, + 3BA08F0325664BCC00449CFF /* SDL_sound_vorbis.c in Sources */, + 3BD2B6982565AEC0003DAD8A /* intrulist.h in Sources */, + 3BD2B6992565AEC0003DAD8A /* keybindings.cpp in Sources */, + 3BD2B69A2565AEC0003DAD8A /* keybindings.h in Sources */, + 3BD2B69B2565AEC0003DAD8A /* lang-fun.h in Sources */, + 3BD2B69C2565AEC0003DAD8A /* lang-fun.mm in Sources */, + 3BD2B69D2565AEC0003DAD8A /* main.mm in Sources */, + 3BD2B69E2565AEC0003DAD8A /* midisource.cpp in Sources */, + 3BD2B69F2565AEC0003DAD8A /* plane.cpp in Sources */, + 3BD2B6A02565AEC0003DAD8A /* plane.h in Sources */, + 3BD2B6A12565AEC0003DAD8A /* quad.h in Sources */, + 3BA08F0925664BCD00449CFF /* SDL_sound_wav.c in Sources */, + 3BA08F0525664BCD00449CFF /* SDL_sound.c in Sources */, + 3BD2B6A22565AEC0003DAD8A /* quadarray.h in Sources */, + 3BD2B6A32565AEC0003DAD8A /* rgssad.cpp in Sources */, + 3BD2B6A42565AEC0003DAD8A /* rgssad.h in Sources */, + 3BD2B6A52565AEC0003DAD8A /* scene.cpp in Sources */, + 3BD2B6A62565AEC0003DAD8A /* scene.h in Sources */, + 3BD2B6A72565AEC0003DAD8A /* sdl-util.h in Sources */, + 3BD2B6A82565AEC0003DAD8A /* sdlsoundsource.cpp in Sources */, + 3BA08EFB25664BCC00449CFF /* SDL_sound_coreaudio.c in Sources */, + 3BD2B6A92565AEC0003DAD8A /* serial-util.h in Sources */, + 3BD2B6AA2565AEC0003DAD8A /* serializable.h in Sources */, + 3BD2B6AB2565AEC0003DAD8A /* settingsmenu.cpp in Sources */, + 3BD2B6AC2565AEC0003DAD8A /* settingsmenu.h in Sources */, + 3BD2B6AD2565AEC0003DAD8A /* shader.cpp in Sources */, + 3BA08EFF25664BCC00449CFF /* SDL_sound_raw.c in Sources */, + 3BD2B6AE2565AEC0003DAD8A /* shader.h in Sources */, + 3BD2B6AF2565AEC0003DAD8A /* sharedmidistate.h in Sources */, + 3BD2B6B02565AEC0003DAD8A /* sharedstate.cpp in Sources */, + 3BD2B6B12565AEC0003DAD8A /* sharedstate.h in Sources */, + 3BD2B6B22565AEC0003DAD8A /* soundemitter.cpp in Sources */, + 3BD2B6B32565AEC0003DAD8A /* soundemitter.h in Sources */, + 3BD2B6B42565AEC0003DAD8A /* sprite.cpp in Sources */, + 3BD2B6B52565AEC0003DAD8A /* sprite.h in Sources */, + 3BD2B6B62565AEC0003DAD8A /* table.cpp in Sources */, + 3BD2B6B72565AEC0003DAD8A /* table.h in Sources */, + 3BA08F0F25664BCD00449CFF /* SDL_sound_aiff.c in Sources */, + 3BA08F0125664BCC00449CFF /* SDL_sound_modplug.c in Sources */, + 3BD2B6B82565AEC0003DAD8A /* texpool.cpp in Sources */, + 3BD2B6B92565AEC0003DAD8A /* texpool.h in Sources */, + 3BD2B6BA2565AEC0003DAD8A /* tileatlas.cpp in Sources */, + 3BD2B6BB2565AEC0003DAD8A /* tileatlas.h in Sources */, + 3BD2B6BC2565AEC0003DAD8A /* tileatlasvx.cpp in Sources */, + 3BD2B6BD2565AEC0003DAD8A /* tileatlasvx.h in Sources */, + 3BD2B6BE2565AEC0003DAD8A /* tilemap-common.h in Sources */, + 3BD2B6BF2565AEC0003DAD8A /* tilemap.cpp in Sources */, + 3BD2B6C02565AEC0003DAD8A /* tilemap.h in Sources */, + 3BD2B6C12565AEC0003DAD8A /* tilemapvx.cpp in Sources */, + 3BD2B6C22565AEC0003DAD8A /* tilemapvx.h in Sources */, + 3BD2B6C32565AEC0003DAD8A /* tilequad.cpp in Sources */, + 3BD2B6C42565AEC0003DAD8A /* tilequad.h in Sources */, + 3BD2B6C52565AEC0003DAD8A /* transform.h in Sources */, + 3BD2B6C62565AEC0003DAD8A /* util.h in Sources */, + 3BD2B6C72565AEC0003DAD8A /* vertex.cpp in Sources */, + 3BD2B6C82565AEC0003DAD8A /* vertex.h in Sources */, + 3BD2B6C92565AEC0003DAD8A /* viewport.cpp in Sources */, + 3BD2B6CA2565AEC0003DAD8A /* viewport.h in Sources */, + 3BD2B6CB2565AEC0003DAD8A /* vorbissource.cpp in Sources */, + 3BD2B6CC2565AEC0003DAD8A /* window.cpp in Sources */, + 3BD2B6CD2565AEC0003DAD8A /* window.h in Sources */, + 3BD2B6CE2565AEC0003DAD8A /* windowvx.cpp in Sources */, + 3BD2B6CF2565AEC0003DAD8A /* windowvx.h in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3BDB25AC2565175000C4A63D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3BD2B44825651952003DAD8A /* audio-binding.cpp in Sources */, + 3BA08F1225664BCD00449CFF /* SDL_sound_flac.c in Sources */, + 3BD2B44925651952003DAD8A /* binding-mri.cpp in Sources */, + 3BD2B44A25651952003DAD8A /* binding-types.h in Sources */, + 3BA08EFA25664BCC00449CFF /* SDL_sound_shn.c in Sources */, + 3BD2B44B25651952003DAD8A /* binding-util.cpp in Sources */, + 3BD2B44C25651952003DAD8A /* binding-util.h in Sources */, + 3BD2B44D25651952003DAD8A /* bitmap-binding.cpp in Sources */, + 3BD2B44E25651952003DAD8A /* cusl-binding.cpp in Sources */, + 3BD2B44F25651952003DAD8A /* disposable-binding.h in Sources */, + 3BD2B45025651952003DAD8A /* etc-binding.cpp in Sources */, + 3BD2B45125651952003DAD8A /* filesystem-binding.cpp in Sources */, + 3BD2B45225651952003DAD8A /* flashable-binding.h in Sources */, + 3BD2B45325651952003DAD8A /* font-binding.cpp in Sources */, + 3BD2B45425651952003DAD8A /* graphics-binding.cpp in Sources */, + 3BD2B45525651952003DAD8A /* input-binding.cpp in Sources */, + 3BD2B45625651952003DAD8A /* miniffi-binding.cpp in Sources */, + 3BD2B45725651952003DAD8A /* module_rpg.cpp in Sources */, + 3BD2B45825651952003DAD8A /* plane-binding.cpp in Sources */, + 3BD2B45925651952003DAD8A /* sceneelement-binding.h in Sources */, + 3BD2B45A25651952003DAD8A /* serializable-binding.h in Sources */, + 3BD2B45B25651952003DAD8A /* sprite-binding.cpp in Sources */, + 3BD2B45C25651952003DAD8A /* table-binding.cpp in Sources */, + 3BD2B45D25651952003DAD8A /* tilemap-binding.cpp in Sources */, + 3BD2B45E25651952003DAD8A /* tilemapvx-binding.cpp in Sources */, + 3BD2B45F25651952003DAD8A /* viewport-binding.cpp in Sources */, + 3BD2B46025651952003DAD8A /* viewportelement-binding.h in Sources */, + 3BD2B46125651952003DAD8A /* window-binding.cpp in Sources */, + 3BD2B46225651952003DAD8A /* windowvx-binding.cpp in Sources */, + 3BD2B3E125651943003DAD8A /* al-util.h in Sources */, + 3BD2B3E225651943003DAD8A /* aldatasource.h in Sources */, + 3BD2B3E325651943003DAD8A /* alstream.cpp in Sources */, + 3BD2B3E425651943003DAD8A /* alstream.h in Sources */, + 3BD2B3E525651943003DAD8A /* audio.cpp in Sources */, + 3BD2B3E625651943003DAD8A /* audio.h in Sources */, + 3BD2B3E725651943003DAD8A /* audiostream.cpp in Sources */, + 3BD2B3E825651943003DAD8A /* audiostream.h in Sources */, + 3BD2B3E925651943003DAD8A /* autotiles.cpp in Sources */, + 3BD2B3EA25651943003DAD8A /* autotilesvx.cpp in Sources */, + 3BD2B3EB25651943003DAD8A /* binding.h in Sources */, + 3BD2B3EC25651943003DAD8A /* bitmap.cpp in Sources */, + 3BD2B3ED25651943003DAD8A /* bitmap.h in Sources */, + 3BD2B3EE25651943003DAD8A /* boost-hash.h in Sources */, + 3BA08F0C25664BCD00449CFF /* SDL_sound_au.c in Sources */, + 3BD2B3EF25651943003DAD8A /* config.h in Sources */, + 3BD2B3F025651943003DAD8A /* config.mm in Sources */, + 3BD2B3F125651943003DAD8A /* debugwriter.h in Sources */, + 3BD2B3F225651943003DAD8A /* disposable.h in Sources */, + 3BD2B4F9256558B6003DAD8A /* CocoaHelpers.mm in Sources */, + 3BA08F0825664BCD00449CFF /* SDL_sound_voc.c in Sources */, + 3BD2B3F325651943003DAD8A /* etc-internal.h in Sources */, + 3BD2B3F425651943003DAD8A /* etc.cpp in Sources */, + 3BD2B3F525651943003DAD8A /* etc.h in Sources */, + 3BD2B3F625651943003DAD8A /* eventthread.cpp in Sources */, + 3BD2B3F725651943003DAD8A /* eventthread.h in Sources */, + 3BD2B3F825651943003DAD8A /* exception.h in Sources */, + 3BD2B3F925651943003DAD8A /* fake-api.h in Sources */, + 3BD2B3FA25651943003DAD8A /* fake-api.mm in Sources */, + 3BD2B3FB25651943003DAD8A /* filesystem.h in Sources */, + 3BD2B3FC25651943003DAD8A /* filesystem.mm in Sources */, + 3BD2B3FD25651943003DAD8A /* flashable.h in Sources */, + 3BD2B3FE25651943003DAD8A /* fluid-fun.cpp in Sources */, + 3BD2B3FF25651943003DAD8A /* fluid-fun.h in Sources */, + 3BD2B40025651943003DAD8A /* font.cpp in Sources */, + 3BD2B40125651943003DAD8A /* font.h in Sources */, + 3BD2B40225651943003DAD8A /* gl-debug.cpp in Sources */, + 3BD2B40325651943003DAD8A /* gl-debug.h in Sources */, + 3BD2B40425651943003DAD8A /* gl-fun.cpp in Sources */, + 3BD2B40525651943003DAD8A /* gl-fun.h in Sources */, + 3BD2B40625651943003DAD8A /* gl-meta.cpp in Sources */, + 3BD2B40725651943003DAD8A /* gl-meta.h in Sources */, + 3BD2B40825651943003DAD8A /* gl-util.h in Sources */, + 3BD2B40925651943003DAD8A /* global-ibo.h in Sources */, + 3BD2B40A25651943003DAD8A /* glstate.cpp in Sources */, + 3BD2B40B25651943003DAD8A /* glstate.h in Sources */, + 3BD2B40C25651943003DAD8A /* graphics.cpp in Sources */, + 3BD2B40D25651943003DAD8A /* graphics.h in Sources */, + 3BA08F0E25664BCD00449CFF /* SDL_sound_mp3.c in Sources */, + 3BD2B40E25651943003DAD8A /* input.cpp in Sources */, + 3BD2B40F25651943003DAD8A /* input.h in Sources */, + 3BA08F0425664BCD00449CFF /* SDL_sound_vorbis.c in Sources */, + 3BD2B41025651943003DAD8A /* intrulist.h in Sources */, + 3BD2B41125651943003DAD8A /* keybindings.cpp in Sources */, + 3BD2B41225651943003DAD8A /* keybindings.h in Sources */, + 3BD2B41325651943003DAD8A /* lang-fun.h in Sources */, + 3BD2B41425651943003DAD8A /* lang-fun.mm in Sources */, + 3BD2B41525651943003DAD8A /* main.mm in Sources */, + 3BD2B41625651943003DAD8A /* midisource.cpp in Sources */, + 3BD2B41725651943003DAD8A /* plane.cpp in Sources */, + 3BD2B41825651943003DAD8A /* plane.h in Sources */, + 3BD2B41925651943003DAD8A /* quad.h in Sources */, + 3BA08F0A25664BCD00449CFF /* SDL_sound_wav.c in Sources */, + 3BA08F0625664BCD00449CFF /* SDL_sound.c in Sources */, + 3BD2B41A25651943003DAD8A /* quadarray.h in Sources */, + 3BD2B41B25651943003DAD8A /* rgssad.cpp in Sources */, + 3BD2B41C25651943003DAD8A /* rgssad.h in Sources */, + 3BD2B41D25651943003DAD8A /* scene.cpp in Sources */, + 3BD2B41E25651943003DAD8A /* scene.h in Sources */, + 3BD2B41F25651943003DAD8A /* sdl-util.h in Sources */, + 3BD2B42025651943003DAD8A /* sdlsoundsource.cpp in Sources */, + 3BA08EFC25664BCC00449CFF /* SDL_sound_coreaudio.c in Sources */, + 3BD2B42125651943003DAD8A /* serial-util.h in Sources */, + 3BD2B42225651943003DAD8A /* serializable.h in Sources */, + 3BD2B42325651943003DAD8A /* settingsmenu.cpp in Sources */, + 3BD2B42425651943003DAD8A /* settingsmenu.h in Sources */, + 3BD2B42525651943003DAD8A /* shader.cpp in Sources */, + 3BA08F0025664BCC00449CFF /* SDL_sound_raw.c in Sources */, + 3BD2B42625651943003DAD8A /* shader.h in Sources */, + 3BD2B42725651943003DAD8A /* sharedmidistate.h in Sources */, + 3BD2B42825651943003DAD8A /* sharedstate.cpp in Sources */, + 3BD2B42925651943003DAD8A /* sharedstate.h in Sources */, + 3BD2B42A25651943003DAD8A /* soundemitter.cpp in Sources */, + 3BD2B42B25651943003DAD8A /* soundemitter.h in Sources */, + 3BD2B42C25651943003DAD8A /* sprite.cpp in Sources */, + 3BD2B42D25651943003DAD8A /* sprite.h in Sources */, + 3BD2B42E25651943003DAD8A /* table.cpp in Sources */, + 3BD2B42F25651943003DAD8A /* table.h in Sources */, + 3BA08F1025664BCD00449CFF /* SDL_sound_aiff.c in Sources */, + 3BA08F0225664BCC00449CFF /* SDL_sound_modplug.c in Sources */, + 3BD2B43025651943003DAD8A /* texpool.cpp in Sources */, + 3BD2B43125651943003DAD8A /* texpool.h in Sources */, + 3BD2B43225651943003DAD8A /* tileatlas.cpp in Sources */, + 3BD2B43325651943003DAD8A /* tileatlas.h in Sources */, + 3BD2B43425651943003DAD8A /* tileatlasvx.cpp in Sources */, + 3BD2B43525651943003DAD8A /* tileatlasvx.h in Sources */, + 3BD2B43625651943003DAD8A /* tilemap-common.h in Sources */, + 3BD2B43725651943003DAD8A /* tilemap.cpp in Sources */, + 3BD2B43825651943003DAD8A /* tilemap.h in Sources */, + 3BD2B43925651943003DAD8A /* tilemapvx.cpp in Sources */, + 3BD2B43A25651943003DAD8A /* tilemapvx.h in Sources */, + 3BD2B43B25651943003DAD8A /* tilequad.cpp in Sources */, + 3BD2B43C25651943003DAD8A /* tilequad.h in Sources */, + 3BD2B43D25651943003DAD8A /* transform.h in Sources */, + 3BD2B43E25651943003DAD8A /* util.h in Sources */, + 3BD2B43F25651943003DAD8A /* vertex.cpp in Sources */, + 3BD2B44025651943003DAD8A /* vertex.h in Sources */, + 3BD2B44125651943003DAD8A /* viewport.cpp in Sources */, + 3BD2B44225651943003DAD8A /* viewport.h in Sources */, + 3BD2B44325651943003DAD8A /* vorbissource.cpp in Sources */, + 3BD2B44425651943003DAD8A /* window.cpp in Sources */, + 3BD2B44525651943003DAD8A /* window.h in Sources */, + 3BD2B44625651943003DAD8A /* windowvx.cpp in Sources */, + 3BD2B44725651943003DAD8A /* windowvx.h in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 3BA08E95256631FD00449CFF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3BA08E902566314300449CFF /* Obtain Dependencies */; + targetProxy = 3BA08E94256631FD00449CFF /* PBXContainerItemProxy */; + }; + 3BA08E972566320100449CFF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3BA08E902566314300449CFF /* Obtain Dependencies */; + targetProxy = 3BA08E962566320100449CFF /* PBXContainerItemProxy */; + }; + 3BA08ED1256643A600449CFF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3BA08EA3256641ED00449CFF /* Assets */; + targetProxy = 3BA08ED0256643A600449CFF /* PBXContainerItemProxy */; + }; + 3BA08ED3256643AD00449CFF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 3BA08EA3256641ED00449CFF /* Assets */; + targetProxy = 3BA08ED2256643AD00449CFF /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 3BA08E922566314300449CFF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 3BA08E932566314300449CFF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 3BA08E9E25663C2E00449CFF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUGGING_SYMBOLS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 3BA08E9F25663C2E00449CFF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 3BA08EA7256641EE00449CFF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = misc/Assets.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.zoro.mkxpz-resources"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + 3BA08EA8256641EE00449CFF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = misc/Assets.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; + MACOSX_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.zoro.mkxpz-resources"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SKIP_INSTALL = YES; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 3BD2B7232565AEC0003DAD8A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BDB240A2564715C00C4A63D /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.3.0; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/frameworks", + ); + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_INPUT_FILETYPE = automatic; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(RUBY_INSTALL_PREFIX)/include/ruby-$(MRI_VERSION)/x86_64-darwin17", + "$(RUBY_INSTALL_PREFIX)/include/ruby-$(MRI_VERSION)", + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib64", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/opt/ruby2.7.0/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + MRI_VERSION = "$(inherited)"; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ( + "$(inherited)", + "-w", + ); + OTHER_CODE_SIGN_FLAGS = "--deep --force"; + OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = com.zoro.mkxpz; + PRODUCT_NAME = "mkxp-z"; + SDKROOT = macosx; + }; + name = Debug; + }; + 3BD2B7242565AEC0003DAD8A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BDB240A2564715C00C4A63D /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.3.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/frameworks", + ); + GCC_C_LANGUAGE_STANDARD = "compiler-default"; + GCC_INPUT_FILETYPE = automatic; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(RUBY_INSTALL_PREFIX)/include/ruby-$(MRI_VERSION)/x86_64-darwin17", + "$(RUBY_INSTALL_PREFIX)/include/ruby-$(MRI_VERSION)", + ); + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib64", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/opt/ruby2.7.0/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + MRI_VERSION = "$(inherited)"; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ( + "$(inherited)", + "-w", + ); + OTHER_CODE_SIGN_FLAGS = "--deep --force"; + OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = com.zoro.mkxpz; + PRODUCT_NAME = "mkxp-z"; + SDKROOT = macosx; + }; + name = Release; + }; + 3BDB22F025644FBF00C4A63D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BDB240A2564715C00C4A63D /* Config.xcconfig */; + buildSettings = { + ARCHS = x86_64; + MACOSX_DEPLOYMENT_TARGET = 10.13; + ONLY_ACTIVE_ARCH = YES; + }; + name = Debug; + }; + 3BDB22F125644FBF00C4A63D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BDB240A2564715C00C4A63D /* Config.xcconfig */; + buildSettings = { + ARCHS = x86_64; + MACOSX_DEPLOYMENT_TARGET = 10.13; + ONLY_ACTIVE_ARCH = YES; + }; + name = Release; + }; + 3BDB25BF2565175400C4A63D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BDB240A2564715C00C4A63D /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.3.0; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + LEGACY_RUBY, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(RUBY_INSTALL_PREFIX)/lib/ruby/1.8/x86_64-darwin17.7.0", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib64", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/opt/ruby1.8.7/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + MRI_VERSION = 1.8.7; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CFLAGS = ( + "$(inherited)", + "-w", + ); + OTHER_CODE_SIGN_FLAGS = "--deep --force"; + OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = com.zoro.mkxpz; + PRODUCT_NAME = "mkxp-z"; + SDKROOT = macosx; + }; + name = Debug; + }; + 3BDB25C02565175400C4A63D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3BDB240A2564715C00C4A63D /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = NO; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = "$(PROJECT_DIR)/entitlements.plist"; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Manual; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1.3.0; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_ASSET_PATHS = ""; + DEVELOPMENT_TEAM = ""; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/frameworks", + ); + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + LEGACY_RUBY, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "$(RUBY_INSTALL_PREFIX)/lib/ruby/1.8/x86_64-darwin17.7.0", + ); + INFOPLIST_FILE = "$(SRCROOT)/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib64", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/lib", + "$(PROJECT_DIR)/MKXPZ-Dependencies/mac/prefix/opt/ruby1.8.7/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 10.13; + MRI_VERSION = 1.8.7; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + OTHER_CFLAGS = ( + "$(inherited)", + "-w", + ); + OTHER_CODE_SIGN_FLAGS = "--deep --force"; + OTHER_LDFLAGS = ""; + PRODUCT_BUNDLE_IDENTIFIER = com.zoro.mkxpz; + PRODUCT_NAME = "mkxp-z"; + SDKROOT = macosx; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 3BA08E912566314300449CFF /* Build configuration list for PBXLegacyTarget "Obtain Dependencies" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BA08E922566314300449CFF /* Debug */, + 3BA08E932566314300449CFF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3BA08E9D25663C2E00449CFF /* Build configuration list for PBXLegacyTarget "Remove Dependencies" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BA08E9E25663C2E00449CFF /* Debug */, + 3BA08E9F25663C2E00449CFF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3BA08EA9256641EE00449CFF /* Build configuration list for PBXNativeTarget "Assets" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BA08EA7256641EE00449CFF /* Debug */, + 3BA08EA8256641EE00449CFF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3BD2B7222565AEC0003DAD8A /* Build configuration list for PBXNativeTarget "Player" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BD2B7232565AEC0003DAD8A /* Debug */, + 3BD2B7242565AEC0003DAD8A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3BDB22EF25644FBF00C4A63D /* Build configuration list for PBXProject "mkxp-z" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BDB22F025644FBF00C4A63D /* Debug */, + 3BDB22F125644FBF00C4A63D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 3BDB25BE2565175400C4A63D /* Build configuration list for PBXNativeTarget "PlayerLegacy" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3BDB25BF2565175400C4A63D /* Debug */, + 3BDB25C02565175400C4A63D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 3BDB22EC25644FBF00C4A63D /* Project object */; +} diff --git a/macos/mkxp-z.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/macos/mkxp-z.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/macos/mkxp-z.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/macos/mkxp-z.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/mkxp-z.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/macos/mkxp-z.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/mkxp-z.xcodeproj/project.xcworkspace/xcuserdata/zoroark.xcuserdatad/UserInterfaceState.xcuserstate b/macos/mkxp-z.xcodeproj/project.xcworkspace/xcuserdata/zoroark.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..fb874c26 Binary files /dev/null and b/macos/mkxp-z.xcodeproj/project.xcworkspace/xcuserdata/zoroark.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/macos/mkxp-z.xcodeproj/xcshareddata/xcschemes/Player.xcscheme b/macos/mkxp-z.xcodeproj/xcshareddata/xcschemes/Player.xcscheme new file mode 100644 index 00000000..5dcd82ee --- /dev/null +++ b/macos/mkxp-z.xcodeproj/xcshareddata/xcschemes/Player.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/mkxp-z.xcodeproj/xcshareddata/xcschemes/PlayerLegacy.xcscheme b/macos/mkxp-z.xcodeproj/xcshareddata/xcschemes/PlayerLegacy.xcscheme new file mode 100644 index 00000000..fb9402a5 --- /dev/null +++ b/macos/mkxp-z.xcodeproj/xcshareddata/xcschemes/PlayerLegacy.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/mkxp-z.xcodeproj/xcuserdata/zoroark.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/macos/mkxp-z.xcodeproj/xcuserdata/zoroark.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 00000000..d35ab780 --- /dev/null +++ b/macos/mkxp-z.xcodeproj/xcuserdata/zoroark.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/macos/mkxp-z.xcodeproj/xcuserdata/zoroark.xcuserdatad/xcschemes/xcschememanagement.plist b/macos/mkxp-z.xcodeproj/xcuserdata/zoroark.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..ab495ca3 --- /dev/null +++ b/macos/mkxp-z.xcodeproj/xcuserdata/zoroark.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,52 @@ + + + + + SchemeUserState + + Obtain Dependencies.xcscheme_^#shared#^_ + + orderHint + 2 + + Player.xcscheme_^#shared#^_ + + orderHint + 1 + + PlayerLegacy.xcscheme_^#shared#^_ + + orderHint + 0 + + Remove Dependencies.xcscheme_^#shared#^_ + + orderHint + 3 + + gen.xcscheme_^#shared#^_ + + orderHint + 2 + + mkxpz-resources.xcscheme_^#shared#^_ + + orderHint + 4 + + + SuppressBuildableAutocreation + + 3BD2B64B2565AEC0003DAD8A + + primary + + + 3BDB22F52564501400C4A63D + + primary + + + + + diff --git a/meson.build b/meson.build index aa745813..d07b6950 100644 --- a/meson.build +++ b/meson.build @@ -25,12 +25,16 @@ win64 = (sizeof['void*'] != sizeof['long']) minimum_macos_version = '' current_macos_version = '' if host_system == 'darwin' - current_macos_version = run_command('sw_vers', '-productVersion').stdout() + current_macos_version = run_command('sw_vers', '-productVersion').stdout().strip() macosx_version_min = get_option('macos_min_version') if macosx_version_min != '' minimum_macos_version = macosx_version_min + else + minimum_macos_version = current_macos_version endif endif + +global_args += '-DMKXPZ_BUILD_MESON' # ==================== # Ext libs # ==================== @@ -72,29 +76,12 @@ endif # BOOST UNORDERED global_include_dirs += include_directories('boost-unordered') -# METAL/GLES +# SDL2_SOUND +subdir('SDL2_sound') + +# GLES gfx_backend = get_option('gfx_backend') -if gfx_backend == 'metal' - # Verify that we can use metal in the first place, - # so 10.13 SDK or higher - message('Verifying that Metal is available...') - if not dependency('Metal', required: false).found() - error('Could not find Metal. Ensure that you are building against the macOS 10.13+ or higher SDK.') - endif - - global_args += '-DGLES2_HEADER' - global_args += '-F' + meson.source_root() + '/macos/lib' - mangle = dependency('MetalANGLE', required: false) - - if not mangle.found() - warning('There is a pre-built archive of MetalANGLE located in macos/lib.') - warning('Simply extract it to the same directory, or build it yourself.') - error('Could not locate the MetalANGLE framework.') - endif - global_args += '-I' + meson.source_root() + '/macos/lib/MetalANGLE.framework/Headers' - global_link_args += '-F' + meson.source_root() + '/macos/lib' - global_dependencies += mangle -elif gfx_backend == 'gles' +if gfx_backend == 'gles' # Needs to be manually set up for now global_args += '-DGLES2_HEADER' elif gfx_backend == 'gl' @@ -147,10 +134,6 @@ if get_option('force32') == true global_args += '-m32' endif -if get_option('mk') - global_args += '-DMARIN' -endif - build_static = false if get_option('static_executable') == true and host_system == 'windows' build_static = true @@ -199,6 +182,9 @@ elif host_system == 'darwin' add_project_arguments('-mmacosx-version-min='+minimum_macos_version, language: ['cpp', 'objc', 'objcpp']) add_project_link_arguments('-mmacosx-version-min='+minimum_macos_version, language: ['cpp', 'objc', 'objcpp']) endif + if minimum_macos_version.version_compare('>=10.13') == true + warning('\nIt seems like you are trying to build for macOS versions that include Metal support.\nPlease consider using the Xcode project located in the `macos` directory instead.') + endif else subdir('linux') add_project_arguments('-std=c++11', language: 'objcpp') diff --git a/mkxp.json.sample b/mkxp.json similarity index 100% rename from mkxp.json.sample rename to mkxp.json diff --git a/src/al-util.h b/src/al-util.h index 51801e78..0789db32 100644 --- a/src/al-util.h +++ b/src/al-util.h @@ -22,7 +22,12 @@ #ifndef ALUTIL_H #define ALUTIL_H +#ifdef MKXPZ_BUILD_XCODE +#include +#else #include +#endif + #include #include diff --git a/src/eventthread.cpp b/src/eventthread.cpp index 4002ca78..a5cb2916 100644 --- a/src/eventthread.cpp +++ b/src/eventthread.cpp @@ -29,11 +29,17 @@ #include #include + +#ifdef MKXPZ_BUILD_XCODE +#include +#include +#else #include #include #ifndef USE_MAC_OPENAL #include #endif +#endif #include "sharedstate.h" #include "graphics.h" diff --git a/src/fake-api.mm b/src/fake-api.mm index 52dc6e22..c669bb0a 100644 --- a/src/fake-api.mm +++ b/src/fake-api.mm @@ -1,3 +1,4 @@ +#ifdef EASY_POKE #import #ifdef __APPLE__ #import @@ -381,3 +382,4 @@ PREFABI LONG MKXP_SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong) { }; #endif +#endif diff --git a/src/font.cpp b/src/font.cpp index 9a4fc197..c2def725 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -31,8 +31,13 @@ #include #include +#ifdef MKXPZ_BUILD_XCODE +#include "CocoaHelpers.hpp" +#endif + #include +#ifndef MKXPZ_BUILD_XCODE #ifndef CJK_FALLBACK #include "liberation.ttf.xxd" #else @@ -59,13 +64,21 @@ BUNDLED_FONT_DECL(liberation) #define BNDL_F_D(f) BUNDLED_FONT_D(f) #define BNDL_F_L(f) BUNDLED_FONT_L(f) -typedef std::pair FontKey; +#endif static SDL_RWops *openBundledFont() { - return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT)); +#ifndef MKXPZ_BUILD_XCODE + return SDL_RWFromConstMem(BNDL_F_D(BUNDLED_FONT), BNDL_F_L(BUNDLED_FONT)); +#else + return SDL_RWFromFile(Cocoa::getFilePath("liberation", "ttf").c_str(), "rb"); +#endif } + + +typedef std::pair FontKey; + struct FontSet { /* 'Regular' style */ diff --git a/src/main.mm b/src/main.mm index 28f0bced..7cd86c33 100644 --- a/src/main.mm +++ b/src/main.mm @@ -19,7 +19,13 @@ ** along with mkxp. If not, see . */ -#import +#ifdef MKXPZ_BUILD_XCODE +#include +#include "CocoaHelpers.hpp" +#else +#include +#import "icon.png.xxd" +#endif #import #import @@ -40,7 +46,7 @@ #import "binding.h" -#ifdef __WINDOWS__ +#if defined(__WINDOWS__) #import "resource.h" #import #endif @@ -49,7 +55,6 @@ #ifdef HAVE_STEAMSHIM #import "steamshim_child.h" #endif -#import "icon.png.xxd" #ifndef THREADED_GLINIT #define GLINIT_SHOWERROR(s) showInitError(s) @@ -146,7 +151,11 @@ static void setupWindowIcon(const Config &conf, SDL_Window *win) { SDL_RWops *iconSrc; if (conf.iconPath.empty()) +#ifndef MKXPZ_BUILD_XCODE iconSrc = SDL_RWFromConstMem(___assets_icon_png, ___assets_icon_png_len); +#else + iconSrc = SDL_RWFromFile(Cocoa::getFilePath("icon", "png").c_str(), "rb"); +#endif else iconSrc = SDL_RWFromFile(conf.iconPath.c_str(), "rb"); @@ -211,18 +220,18 @@ int main(int argc, char *argv[]) { } } @try { - [fm changeCurrentDirectoryPath:@(dataDir)]; + [fm changeCurrentDirectoryPath: [OFString stringWithUTF8String:dataDir ]]; } @catch (...) { } #endif - + /* now we load the config */ Config conf; conf.read(argc, argv); if (!conf.gameFolder.empty()) { @try { - [fm changeCurrentDirectoryPath:@(conf.gameFolder.c_str())]; + [fm changeCurrentDirectoryPath: [OFString stringWithUTF8String: conf.gameFolder.c_str()]]; } @catch (...) { showInitError(std::string("Unable to switch into gameFolder ") + conf.gameFolder); diff --git a/src/meson.build b/src/meson.build index c32c9580..8b371fb9 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,7 +5,6 @@ vorbisfile = dependency('vorbisfile', static: build_static) sdl2 = dependency('sdl2', static: build_static) sdl2_ttf = dependency('SDL2_ttf', static: build_static) sdl2_image = dependency('SDL2_image', static: build_static) -sdl_sound = dependency('SDL_sound', static: build_static) openal = dependency('openal', static: build_static) zlib = dependency('zlib', static: build_static) @@ -38,7 +37,7 @@ endif global_args += '-DALCDEVICE_STRUCT=' + alcdev_struct global_include_dirs += include_directories('.') -global_dependencies += [sigcxx, openal, zlib, pixman, physfs, vorbisfile, sdl2, sdl2_ttf, sdl2_image, sdl_sound] +global_dependencies += [sigcxx, openal, zlib, pixman, physfs, vorbisfile, sdl2, sdl2_ttf, sdl2_image] if host_system == 'darwin' global_dependencies += compilers['cpp'].find_library('iconv') global_dependencies += dependency('Foundation') diff --git a/src/shader.cpp b/src/shader.cpp index b050e8f0..a558109c 100644 --- a/src/shader.cpp +++ b/src/shader.cpp @@ -28,6 +28,7 @@ #include #include +#ifndef MKXPZ_BUILD_XCODE #include "common.h.xxd" #include "sprite.frag.xxd" #include "hue.frag.xxd" @@ -53,16 +54,30 @@ #include "blurH.vert.xxd" #include "blurV.vert.xxd" #include "tilemapvx.vert.xxd" +#endif - +#ifdef MKXPZ_BUILD_XCODE +#include "CocoaHelpers.hpp" +#define INIT_SHADER(vert, frag, name) \ +{ \ + std::string v = Cocoa::getFile("Shaders/" #vert, "vert"); \ + std::string f = Cocoa::getFile("Shaders/" #frag, "frag"); \ + Shader::init((const unsigned char*)v.c_str(), v.length(), (const unsigned char*)f.c_str(), f.length(), #vert, #frag, #name); \ +} +#else #define INIT_SHADER(vert, frag, name) \ { \ Shader::init(___shader_##vert##_vert, ___shader_##vert##_vert_len, ___shader_##frag##_frag, ___shader_##frag##_frag_len, \ #vert, #frag, #name); \ } +#endif #define GET_U(name) u_##name = gl.GetUniformLocation(program, #name) +#ifdef MKXPZ_BUILD_XCODE + std::string Shader::shaderCommon = ""; +#endif + static void printShaderLog(GLuint shader) { GLint logLength; @@ -87,6 +102,10 @@ static void printProgramLog(GLuint program) Shader::Shader() { +#ifdef MKXPZ_BUILD_XCODE + if (Shader::shaderCommon.empty()) + Shader::shaderCommon = Cocoa::getFile("Shaders/common", "h"); +#endif vertShader = gl.CreateShader(GL_VERTEX_SHADER); fragShader = gl.CreateShader(GL_FRAGMENT_SHADER); @@ -112,6 +131,12 @@ void Shader::unbind() glState.program.set(0); } +#ifdef MKXPZ_BUILD_XCODE +std::string &Shader::commonHeader() { + return Shader::shaderCommon; +} +#endif + static void setupShaderSource(GLuint shader, GLenum type, const unsigned char *body, int bodySize) { @@ -136,8 +161,13 @@ static void setupShaderSource(GLuint shader, GLenum type, ++i; } +#ifndef MKXPZ_BUILD_XCODE shaderSrc[i] = (const GLchar*) ___shader_common_h; shaderSrcSize[i] = ___shader_common_h_len; +#else + shaderSrc[i] = (const GLchar*) Shader::commonHeader().c_str(); + shaderSrcSize[i] = Shader::commonHeader().length(); +#endif ++i; shaderSrc[i] = (const GLchar*) body; diff --git a/src/shader.h b/src/shader.h index cdfc9f6e..8d828434 100644 --- a/src/shader.h +++ b/src/shader.h @@ -38,13 +38,15 @@ public: TexCoord = 1, Color = 2 }; + + static std::string &commonHeader(); protected: Shader(); ~Shader(); - void init(const unsigned char *vert, int vertSize, - const unsigned char *frag, int fragSize, + void init(const unsigned char *vert, int vertSize, + const unsigned char *frag, int fragSize, const char *vertName, const char *fragName, const char *programName); void initFromFile(const char *vertFile, const char *fragFile, @@ -55,6 +57,11 @@ protected: GLuint vertShader, fragShader; GLuint program; + +private: +#ifdef MKXPZ_BUILD_XCODE + static std::string shaderCommon; +#endif }; class ShaderBase : public Shader