Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dosbox-staging/dosbox-staging.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkrcroft <krcroft@users.noreply.github.com>2019-12-11 20:06:36 +0300
committerPatryk Obara <dreamer.tan@gmail.com>2020-01-08 17:26:21 +0300
commit9aec7c324fa047da584d0c034d792e1e6064866f (patch)
tree443f45a1b303d916c29edaef167d15b666b9fe08
parentd88f6db3b9193d276756b01703c6e9822e8646e5 (diff)
Add support for FLAC, Opus, and MP3 audio tracksvogons/ece-r4305-1
Also, enable support for MSF values in cue files. Imported-from: https://www.vogons.org/viewtopic.php?p=695839#p695839
-rw-r--r--Makefile.am22
-rw-r--r--configure.ac18
-rw-r--r--src/Makefile.am14
-rw-r--r--src/dos/cdrom.h101
-rw-r--r--src/dos/cdrom_image.cpp660
-rw-r--r--src/dos/drive_iso.cpp6
-rw-r--r--src/libs/Makefile.am2
-rw-r--r--src/libs/decoders/Makefile.am58
-rw-r--r--src/libs/decoders/SDL_sound.c833
-rw-r--r--src/libs/decoders/SDL_sound.h748
-rw-r--r--src/libs/decoders/SDL_sound_internal.h333
-rw-r--r--src/libs/decoders/archive.h346
-rw-r--r--src/libs/decoders/audio_convert.c732
-rw-r--r--src/libs/decoders/docs/copying.txt438
-rw-r--r--src/libs/decoders/docs/credits.txt66
-rw-r--r--src/libs/decoders/docs/license.txt526
-rw-r--r--src/libs/decoders/dr_flac.h11032
-rw-r--r--src/libs/decoders/dr_mp3.h4190
-rw-r--r--src/libs/decoders/dr_wav.h5334
-rw-r--r--src/libs/decoders/flac.c186
-rw-r--r--src/libs/decoders/internal/Makefile210
-rw-r--r--src/libs/decoders/mp3.cpp223
-rw-r--r--src/libs/decoders/mp3_seek_table.cpp339
-rw-r--r--src/libs/decoders/mp3_seek_table.h57
-rw-r--r--src/libs/decoders/opus.c665
-rw-r--r--src/libs/decoders/stb.h14453
-rw-r--r--src/libs/decoders/stb_vorbis.h5624
-rw-r--r--src/libs/decoders/stb_vorbis_test.c262
-rw-r--r--src/libs/decoders/vorbis.c238
-rw-r--r--src/libs/decoders/wav.c171
-rw-r--r--src/libs/decoders/xxh3.h1652
-rw-r--r--src/libs/decoders/xxhash.c1114
-rw-r--r--src/libs/decoders/xxhash.h548
33 files changed, 50970 insertions, 231 deletions
diff --git a/Makefile.am b/Makefile.am
index 31fcda102..51be4ce2e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -2,3 +2,25 @@
EXTRA_DIST = autogen.sh
SUBDIRS = src include docs visualc_net
+
+CLEANFILES = \
+ Makefile \
+ Makefile.in \
+ config.guess \
+ config.sub \
+ install-sh \
+ aclocal.m4 \
+ configure \
+ config.h \
+ config.h.in \
+ ltmain.sh \
+ compile \
+ missing \
+ depcomp \
+ config.status \
+ stamp-h1 \
+ libtool \
+ config.log
+
+clean-local:
+ -rm -rf autom4te.cache
diff --git a/configure.ac b/configure.ac
index d7e9b3a72..4a44dbaab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -559,22 +559,7 @@ case "$host" in
esac
fi
-AH_TEMPLATE(C_SDL_SOUND,[Define to 1 to enable SDL_sound support])
-AC_CHECK_HEADER(SDL_sound.h,have_SDL_sound_h=yes,)
-AC_CHECK_LIB(SDL_sound, Sound_Init, have_SDL_sound_init=yes,,)
-AC_CHECK_LIB(SDL_sound, Sound_Seek, have_SDL_sound_seek=yes,,)
-if test x$have_SDL_sound_h = xyes -a x$have_SDL_sound_init = xyes ; then
- if test x$have_SDL_sound_seek = xyes ; then
- LIBS="-lSDL_sound $LIBS"
- AC_DEFINE(C_SDL_SOUND,1)
- else
- AC_MSG_WARN([Can't find SoundSeek in libSDL_Sound, libSDL_sound support disabled])
- fi
-else
- AC_MSG_WARN([Can't find libSDL_sound, libSDL_sound support disabled])
-fi
-
-dnl Check for mprotect. Needed for 64 bits linux
+dnl Check for mprotect. Needed for 64 bits linux
AH_TEMPLATE(C_HAVE_MPROTECT,[Define to 1 if you have the mprotect function])
AC_CHECK_HEADER([sys/mman.h], [
AC_CHECK_FUNC([mprotect],[AC_DEFINE(C_HAVE_MPROTECT,1)])
@@ -663,6 +648,7 @@ src/ints/Makefile
src/libs/Makefile
src/libs/zmbv/Makefile
src/libs/gui_tk/Makefile
+src/libs/decoders/Makefile
src/misc/Makefile
src/shell/Makefile
src/platform/Makefile
diff --git a/src/Makefile.am b/src/Makefile.am
index 3869353aa..7e8920120 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
-SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell platform
+SUBDIRS = cpu debug dos fpu gui hardware libs ints misc shell platform
bin_PROGRAMS = dosbox
@@ -14,7 +14,17 @@ endif
dosbox_SOURCES = dosbox.cpp $(ico_stuff)
dosbox_LDADD = cpu/libcpu.a debug/libdebug.a dos/libdos.a fpu/libfpu.a hardware/libhardware.a gui/libgui.a \
ints/libints.a misc/libmisc.a shell/libshell.a hardware/mame/libmame.a \
- hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a
+ hardware/serialport/libserial.a libs/gui_tk/libgui_tk.a libs/decoders/libdecoders.a \
+ libs/decoders/internal/lib/libopusfile.a libs/decoders/internal/lib/libspeexdsp.a \
+ libs/decoders/internal/lib/libopus.a libs/decoders/internal/lib/libogg.a
+
+libs/decoders/internal/lib/libopusfile.a:
+ cd libs/decoders/internal \
+ && $(MAKE) -j$(THREADS) lib/libopusfile.a
+
+libs/decoders/internal/lib/libspeexdsp.a:
+ cd libs/decoders/internal \
+ && $(MAKE) -j$(THREADS) lib/libspeexdsp.a
EXTRA_DIST = winres.rc dosbox.ico
diff --git a/src/dos/cdrom.h b/src/dos/cdrom.h
index c1219c7a9..e79fe2b44 100644
--- a/src/dos/cdrom.h
+++ b/src/dos/cdrom.h
@@ -31,15 +31,16 @@
#include "dosbox.h"
#include "mem.h"
#include "mixer.h"
-#include "SDL.h"
-#include "SDL_thread.h"
+#include <SDL.h>
+#include <SDL_thread.h>
-#if defined(C_SDL_SOUND)
-#include "SDL_sound.h"
-#endif
+#include "../libs/decoders/SDL_sound.h"
#define RAW_SECTOR_SIZE 2352
#define COOKED_SECTOR_SIZE 2048
+#define AUDIO_DECODE_BUFFER_SIZE 16512
+// 16512 is 16384 + 128, enough for four 4KB decode audio chunks plus 128 bytes extra
+// which accomodate the leftovers from typically callbacks, which minimizes our wrap size.
enum { CDROM_USE_SDL, CDROM_USE_ASPI, CDROM_USE_IOCTL_DIO, CDROM_USE_IOCTL_DX, CDROM_USE_IOCTL_MCI };
@@ -135,37 +136,51 @@ class CDROM_Interface_Image : public CDROM_Interface
{
private:
class TrackFile {
+ protected:
+ TrackFile(Bit16u chunkSize) : chunkSize(chunkSize) {}
public:
- virtual bool read(Bit8u *buffer, int seek, int count) = 0;
- virtual int getLength() = 0;
- virtual ~TrackFile() { };
+ virtual bool read(Bit8u *buffer, int seek, int count) = 0;
+ virtual bool seek(Bit32u offset) = 0;
+ virtual Bit16u decode(Bit8u *buffer) = 0;
+ virtual Bit16u getEndian() = 0;
+ virtual Bit32u getRate() = 0;
+ virtual Bit8u getChannels() = 0;
+ virtual int getLength() = 0;
+ virtual ~TrackFile() { };
+ const Bit16u chunkSize = 0;
};
class BinaryFile : public TrackFile {
public:
BinaryFile(const char *filename, bool &error);
~BinaryFile();
- bool read(Bit8u *buffer, int seek, int count);
- int getLength();
+ bool read(Bit8u *buffer, int seek, int count);
+ bool seek(Bit32u offset);
+ Bit16u decode(Bit8u *buffer);
+ Bit16u getEndian();
+ Bit32u getRate() { return 44100; }
+ Bit8u getChannels() { return 2; }
+ int getLength();
private:
BinaryFile();
std::ifstream *file;
};
- #if defined(C_SDL_SOUND)
class AudioFile : public TrackFile {
public:
AudioFile(const char *filename, bool &error);
~AudioFile();
- bool read(Bit8u *buffer, int seek, int count);
- int getLength();
+ bool read(Bit8u *buffer, int seek, int count) { return false; }
+ bool seek(Bit32u offset);
+ Bit16u decode(Bit8u *buffer);
+ Bit16u getEndian();
+ Bit32u getRate();
+ Bit8u getChannels();
+ int getLength();
private:
AudioFile();
Sound_Sample *sample;
- int lastCount;
- int lastSeek;
};
- #endif
struct Track {
int number;
@@ -179,24 +194,24 @@ private:
};
public:
- CDROM_Interface_Image (Bit8u subUnit);
- virtual ~CDROM_Interface_Image (void);
- void InitNewMedia (void);
- bool SetDevice (char* path, int forceCD);
- bool GetUPC (unsigned char& attr, char* upc);
- bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
- bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
- bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
- bool GetAudioStatus (bool& playing, bool& pause);
- bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
- bool PlayAudioSector (unsigned long start,unsigned long len);
- bool PauseAudio (bool resume);
- bool StopAudio (void);
- void ChannelControl (TCtrl ctrl);
- bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
- bool LoadUnloadMedia (bool unload);
- bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector);
- bool HasDataTrack (void);
+ CDROM_Interface_Image (Bit8u subUnit);
+ virtual ~CDROM_Interface_Image (void);
+ void InitNewMedia (void);
+ bool SetDevice (char* path, int forceCD);
+ bool GetUPC (unsigned char& attr, char* upc);
+ bool GetAudioTracks (int& stTrack, int& end, TMSF& leadOut);
+ bool GetAudioTrackInfo (int track, TMSF& start, unsigned char& attr);
+ bool GetAudioSub (unsigned char& attr, unsigned char& track, unsigned char& index, TMSF& relPos, TMSF& absPos);
+ bool GetAudioStatus (bool& playing, bool& pause);
+ bool GetMediaTrayStatus (bool& mediaPresent, bool& mediaChanged, bool& trayOpen);
+ bool PlayAudioSector (unsigned long start,unsigned long len);
+ bool PauseAudio (bool resume);
+ bool StopAudio (void);
+ void ChannelControl (TCtrl ctrl);
+ bool ReadSectors (PhysPt buffer, bool raw, unsigned long sector, unsigned long num);
+ bool LoadUnloadMedia (bool unload);
+ bool ReadSector (Bit8u *buffer, bool raw, unsigned long sector);
+ bool HasDataTrack (void);
static CDROM_Interface_Image* images[26];
@@ -208,15 +223,20 @@ static void CDAudioCallBack(Bitu len);
static struct imagePlayer {
CDROM_Interface_Image *cd;
MixerChannel *channel;
- SDL_mutex *mutex;
- Bit8u buffer[8192];
- int bufLen;
- int currFrame;
- int targetFrame;
+ Bit8u buffer[AUDIO_DECODE_BUFFER_SIZE];
+ Bit32u startFrame;
+ Bit32u currFrame;
+ Bit32u numFrames;
bool isPlaying;
bool isPaused;
bool ctrlUsed;
TCtrl ctrlData;
+ TrackFile* trackFile;
+ void (MixerChannel::*addSamples) (Bitu, const Bit16s*);
+ Bit32u playbackTotal;
+ int playbackRemaining;
+ Bit16u bufferPos;
+ Bit16u bufferConsumed;
} player;
void ClearTracks();
@@ -228,6 +248,7 @@ static struct imagePlayer {
bool GetCueKeyword(std::string &keyword, std::istream &in);
bool GetCueFrame(int &frames, std::istream &in);
bool GetCueString(std::string &str, std::istream &in);
+ // bool AddTrack(Track &curr, int &shift, int prestart, int &totalPregap, int currPregap, int frameFromCue);
bool AddTrack(Track &curr, int &shift, int prestart, int &totalPregap, int currPregap);
static int refCount;
@@ -363,7 +384,7 @@ private:
CDROM_Interface_Ioctl *cd;
MixerChannel *channel;
SDL_mutex *mutex;
- Bit8u buffer[8192];
+ Bit8u buffer[AUDIO_DECODE_BUFFER_SIZE];
int bufLen;
int currFrame;
int targetFrame;
diff --git a/src/dos/cdrom_image.cpp b/src/dos/cdrom_image.cpp
index d669462a9..cd7ef209d 100644
--- a/src/dos/cdrom_image.cpp
+++ b/src/dos/cdrom_image.cpp
@@ -16,6 +16,11 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+// #define DEBUG 1
+#ifdef DEBUG
+#include <time.h>
+#include <chrono>
+#endif
#include <cctype>
#include <cmath>
@@ -38,12 +43,32 @@
#include <string.h>
#endif
+#if defined(WORDS_BIGENDIAN)
+#define IS_BIGENDIAN true
+#else
+#define IS_BIGENDIAN false
+#endif
+
using namespace std;
#define MAX_LINE_LENGTH 512
#define MAX_FILENAME_LENGTH 256
+#ifdef DEBUG
+char* get_time() {
+ static time_t rawtime;
+ struct tm* ptime;
+ static char time_str[] = "00:00:00";
+
+ time(&rawtime);
+ ptime = localtime(&rawtime);
+ sprintf(time_str, "%02d:%02d:%02d", ptime->tm_hour, ptime->tm_min, ptime->tm_sec);
+ return time_str;
+}
+#endif
+
CDROM_Interface_Image::BinaryFile::BinaryFile(const char *filename, bool &error)
+ :TrackFile(RAW_SECTOR_SIZE)
{
file = new ifstream(filename, ios::in | ios::binary);
error = (file == NULL) || (file->fail());
@@ -70,14 +95,42 @@ int CDROM_Interface_Image::BinaryFile::getLength()
return length;
}
-#if defined(C_SDL_SOUND)
+Bit16u CDROM_Interface_Image::BinaryFile::getEndian()
+{
+ // Image files are read into native-endian byte-order
+ #if defined(WORDS_BIGENDIAN)
+ return AUDIO_S16MSB;
+ #else
+ return AUDIO_S16LSB;
+ #endif
+}
+
+
+bool CDROM_Interface_Image::BinaryFile::seek(Bit32u offset)
+{
+ file->seekg(offset, ios::beg);
+ return !file->fail();
+}
+
+Bit16u CDROM_Interface_Image::BinaryFile::decode(Bit8u *buffer)
+{
+ file->read((char*)buffer, chunkSize);
+ return file->gcount();
+}
+
CDROM_Interface_Image::AudioFile::AudioFile(const char *filename, bool &error)
+ :TrackFile(4096)
{
- Sound_AudioInfo desired = {AUDIO_S16, 2, 44100};
- sample = Sound_NewSampleFromFile(filename, &desired, RAW_SECTOR_SIZE);
- lastCount = RAW_SECTOR_SIZE;
- lastSeek = 0;
- error = (sample == NULL);
+ // Use the audio file's actual sample rate and number of channels as opposed to overriding
+ Sound_AudioInfo desired = {AUDIO_S16, 0, 0};
+ sample = Sound_NewSampleFromFile(filename, &desired, chunkSize);
+ if (sample) {
+ error = false;
+ std::string filename_only(filename);
+ filename_only = filename_only.substr(filename_only.find_last_of("\\/") + 1);
+ LOG_MSG("CDROM: Loaded %s [%d Hz %d-channel]", filename_only.c_str(), this->getRate(), this->getChannels());
+ } else
+ error = true;
}
CDROM_Interface_Image::AudioFile::~AudioFile()
@@ -85,65 +138,105 @@ CDROM_Interface_Image::AudioFile::~AudioFile()
Sound_FreeSample(sample);
}
-bool CDROM_Interface_Image::AudioFile::read(Bit8u *buffer, int seek, int count)
+bool CDROM_Interface_Image::AudioFile::seek(Bit32u offset)
{
- if (lastCount != count) {
- int success = Sound_SetBufferSize(sample, count);
- if (!success) return false;
- }
- if (lastSeek != (seek - count)) {
- int success = Sound_Seek(sample, (int)((double)(seek) / 176.4f));
- if (!success) return false;
+ #ifdef DEBUG
+ const auto begin = std::chrono::steady_clock::now();
+ #endif
+
+ // Convert the byte-offset to a time offset (milliseconds)
+ const bool result = Sound_Seek(sample, lround(offset/176.4f));
+
+ #ifdef DEBUG
+ const auto end = std::chrono::steady_clock::now();
+ LOG_MSG("%s CDROM: seek(%u) took %f ms", get_time(), offset, chrono::duration <double, milli> (end - begin).count());
+ #endif
+
+ return result;
+}
+
+Bit16u CDROM_Interface_Image::AudioFile::decode(Bit8u *buffer)
+{
+ const Bit16u bytes = Sound_Decode(sample);
+ memcpy(buffer, sample->buffer, bytes);
+ return bytes;
+}
+
+Bit16u CDROM_Interface_Image::AudioFile::getEndian()
+{
+ return sample->actual.format;
+}
+
+Bit32u CDROM_Interface_Image::AudioFile::getRate()
+{
+ Bit32u rate(0);
+ if (sample) {
+ rate = sample->actual.rate;
}
- lastSeek = seek;
- int bytes = Sound_Decode(sample);
- if (bytes < count) {
- memcpy(buffer, sample->buffer, bytes);
- memset(buffer + bytes, 0, count - bytes);
- } else {
- memcpy(buffer, sample->buffer, count);
+ return rate;
+}
+
+Bit8u CDROM_Interface_Image::AudioFile::getChannels()
+{
+ Bit8u channels(0);
+ if (sample) {
+ channels = sample->actual.channels;
}
-
- return !(sample->flags & SOUND_SAMPLEFLAG_ERROR);
+ return channels;
}
int CDROM_Interface_Image::AudioFile::getLength()
{
- int time = 1;
- int shift = 0;
- if (!(sample->flags & SOUND_SAMPLEFLAG_CANSEEK)) return -1;
-
- while (true) {
- int success = Sound_Seek(sample, (unsigned int)(shift + time));
- if (!success) {
- if (time == 1) return lround((double)shift * 176.4f);
- shift += time >> 1;
- time = 1;
- } else {
- if (time > ((numeric_limits<int>::max() - shift) / 2)) return -1;
- time = time << 1;
- }
+ int length(-1);
+
+ // GetDuration returns milliseconds ... but getLength needs Red Book bytes.
+ const int duration_ms = Sound_GetDuration(sample);
+ if (duration_ms > 0) {
+ // ... so convert ms to "Red Book bytes" by multiplying with 176.4f,
+ // which is 44,100 samples/second * 2-channels * 2 bytes/sample
+ // / 1000 milliseconds/second
+ length = round(duration_ms * 176.4f);
}
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: AudioFile::getLength is %d bytes", get_time(), length);
+ #endif
+
+ return length;
}
-#endif
// initialize static members
int CDROM_Interface_Image::refCount = 0;
CDROM_Interface_Image* CDROM_Interface_Image::images[26] = {};
CDROM_Interface_Image::imagePlayer CDROM_Interface_Image::player = {
- NULL, NULL, NULL, {0}, 0, 0, 0, false, false, false, {0} };
-
+ NULL, // CDROM_Interface_Image*
+ NULL, // MixerChannel*
+ {0}, // buffer[]
+ 0, // startFrame
+ 0, // currFrame
+ 0, // numFrames
+ false, // isPlaying
+ false, // isPaused
+ false, // ctrlUsed
+ {0}, // ctrlData struct
+ NULL, // activeTrack
+ NULL, // addSamples
+ 0, // playbackTotal
+ 0, // playbackRemaining
+ 0, // bufferPos
+ 0 // bufferConsumed
+};
CDROM_Interface_Image::CDROM_Interface_Image(Bit8u subUnit)
- :subUnit(subUnit)
+ :subUnit(subUnit)
{
images[subUnit] = this;
if (refCount == 0) {
- player.mutex = SDL_CreateMutex();
- if (!player.channel) {
- player.channel = MIXER_AddChannel(&CDAudioCallBack, 44100, "CDAUDIO");
+ if (player.channel == NULL) {
+ // channel is kept dormant except during cdrom playback periods
+ player.channel = MIXER_AddChannel(&CDAudioCallBack, 0, "CDAUDIO");
+ player.channel->Enable(false);
+ // LOG_MSG("CDROM: Initialized with %d-byte circular buffer", AUDIO_DECODE_BUFFER_SIZE);
}
- player.channel->Enable(true);
}
refCount++;
}
@@ -154,8 +247,10 @@ CDROM_Interface_Image::~CDROM_Interface_Image()
if (player.cd == this) player.cd = NULL;
ClearTracks();
if (refCount == 0) {
- SDL_DestroyMutex(player.mutex);
- player.channel->Enable(false);
+ StopAudio();
+ MIXER_DelChannel(player.channel);
+ player.channel = NULL;
+ // LOG_MSG("CDROM: Audio channel freed");
}
}
@@ -180,6 +275,11 @@ bool CDROM_Interface_Image::GetUPC(unsigned char& attr, char* upc)
{
attr = 0;
strcpy(upc, this->mcn.c_str());
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: GetUPC=%s", get_time(), upc);
+ #endif
+
return true;
}
@@ -188,6 +288,17 @@ bool CDROM_Interface_Image::GetAudioTracks(int& stTrack, int& end, TMSF& leadOut
stTrack = 1;
end = (int)(tracks.size() - 1);
FRAMES_TO_MSF(tracks[tracks.size() - 1].start + 150, &leadOut.min, &leadOut.sec, &leadOut.fr);
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: GetAudioTracks, stTrack=%d, end=%d, leadOut.min=%d, leadOut.sec=%d, leadOut.fr=%d",
+ get_time(),
+ stTrack,
+ end,
+ leadOut.min,
+ leadOut.sec,
+ leadOut.fr);
+ #endif
+
return true;
}
@@ -196,6 +307,18 @@ bool CDROM_Interface_Image::GetAudioTrackInfo(int track, TMSF& start, unsigned c
if (track < 1 || track > (int)tracks.size()) return false;
FRAMES_TO_MSF(tracks[track - 1].start + 150, &start.min, &start.sec, &start.fr);
attr = tracks[track - 1].attr;
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: GetAudioTrackInfo track=%d MSF %02d:%02d:%02d, attr=%u",
+ get_time(),
+ track,
+ start.min,
+ start.sec,
+ start.fr,
+ attr
+ );
+ #endif
+
return true;
}
@@ -207,7 +330,25 @@ bool CDROM_Interface_Image::GetAudioSub(unsigned char& attr, unsigned char& trac
attr = tracks[track - 1].attr;
index = 1;
FRAMES_TO_MSF(player.currFrame + 150, &absPos.min, &absPos.sec, &absPos.fr);
- FRAMES_TO_MSF(player.currFrame - tracks[track - 1].start, &relPos.min, &relPos.sec, &relPos.fr);
+ FRAMES_TO_MSF(player.currFrame - tracks[track - 1].start + 150, &relPos.min, &relPos.sec, &relPos.fr);
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: GetAudioSub attr=%u, track=%u, index=%u", get_time(), attr, track, index);
+
+ LOG_MSG("%s CDROM: GetAudioSub absoute offset (%d), MSF=%d:%d:%d",
+ get_time(),
+ player.currFrame + 150,
+ absPos.min,
+ absPos.sec,
+ absPos.fr);
+ LOG_MSG("%s CDROM: GetAudioSub relative offset (%d), MSF=%d:%d:%d",
+ get_time(),
+ player.currFrame - tracks[track - 1].start + 150,
+ relPos.min,
+ relPos.sec,
+ relPos.fr);
+ #endif
+
return true;
}
@@ -215,6 +356,11 @@ bool CDROM_Interface_Image::GetAudioStatus(bool& playing, bool& pause)
{
playing = player.isPlaying;
pause = player.isPaused;
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: GetAudioStatus playing=%d, paused=%d", get_time(), playing, pause);
+ #endif
+
return true;
}
@@ -223,39 +369,126 @@ bool CDROM_Interface_Image::GetMediaTrayStatus(bool& mediaPresent, bool& mediaCh
mediaPresent = true;
mediaChanged = false;
trayOpen = false;
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: GetMediaTrayStatus present=%d, changed=%d, open=%d", get_time(), mediaPresent, mediaChanged, trayOpen);
+ #endif
+
return true;
}
-bool CDROM_Interface_Image::PlayAudioSector(unsigned long start,unsigned long len)
+bool CDROM_Interface_Image::PlayAudioSector(unsigned long start, unsigned long len)
{
- // We might want to do some more checks. E.g valid start and length
- SDL_mutexP(player.mutex);
- player.cd = this;
- player.currFrame = start;
- player.targetFrame = start + len;
- int track = GetTrack(start) - 1;
- if(track >= 0 && tracks[track].attr == 0x40) {
+ bool is_playable(false);
+ const int track = GetTrack(start) - 1;
+
+ // The CDROM Red Book standard allows up to 99 tracks, which includes the data track
+ if ( track < 0 || track > 99 )
+ LOG(LOG_MISC, LOG_WARN)("Game tried to load track #%d, which is invalid", track);
+
+ // Attempting to play zero sectors is a no-op
+ else if (len == 0)
+ LOG(LOG_MISC, LOG_WARN)("Game tried to play zero sectors, skipping");
+
+ // The maximum storage achieved on a CDROM was ~900MB or just under 100 minutes
+ // with overburning, so use this threshold to sanity-check the start sector.
+ else if (start > 450000)
+ LOG(LOG_MISC, LOG_WARN)("Game tried to read sector %lu, which is beyond the 100-minute maximum of a CDROM", start);
+
+ // We can't play audio from a data track (as it would result in garbage/static)
+ else if(track >= 0 && tracks[track].attr == 0x40)
LOG(LOG_MISC,LOG_WARN)("Game tries to play the data track. Not doing this");
- player.isPlaying = false;
- //Unclear wether return false should be here.
- //specs say that this function returns at once and games should check the status wether the audio is actually playing
- //Real drives either fail or succeed as well
- } else player.isPlaying = true;
- player.isPaused = false;
- SDL_mutexV(player.mutex);
- return true;
+
+ // Checks passed, setup the audio stream
+ else {
+ TrackFile* trackFile = tracks[track].file;
+
+ // Convert the playback start sector to a time offset (milliseconds) relative to the track
+ const Bit32u offset = tracks[track].skip + (start - tracks[track].start) * tracks[track].sectorSize;
+ is_playable = trackFile->seek(offset);
+
+ // only initialize the player elements if our track is playable
+ if (is_playable) {
+ const Bit8u channels = trackFile->getChannels();
+ const Bit32u rate = trackFile->getRate();
+
+ player.cd = this;
+ player.trackFile = trackFile;
+ player.startFrame = start;
+ player.currFrame = start;
+ player.numFrames = len;
+ player.bufferPos = 0;
+ player.bufferConsumed = 0;
+ player.isPlaying = true;
+ player.isPaused = false;
+
+ if ( (!IS_BIGENDIAN && trackFile->getEndian() == AUDIO_S16SYS) ||
+ ( IS_BIGENDIAN && trackFile->getEndian() != AUDIO_S16SYS) )
+ player.addSamples = channels == 2 ? &MixerChannel::AddSamples_s16 \
+ : &MixerChannel::AddSamples_m16;
+ else
+ player.addSamples = channels == 2 ? &MixerChannel::AddSamples_s16_nonnative \
+ : &MixerChannel::AddSamples_m16_nonnative;
+
+ const float bytesPerMs = rate * channels * 2 / 1000.0;
+ player.playbackTotal = lround(len * tracks[track].sectorSize * bytesPerMs / 176.4);
+ player.playbackRemaining = player.playbackTotal;
+
+ #ifdef DEBUG
+ LOG_MSG(
+ "%s CDROM: Playing track %d at %.1f KHz %d-channel at start sector %lu (%.1f minute-mark), seek %u (skip=%d,dstart=%d,secsize=%d), for %lu sectors (%.1f seconds)",
+ get_time(),
+ track,
+ rate/1000.0,
+ channels,
+ start,
+ offset * (1/10584000.0),
+ offset,
+ tracks[track].skip,
+ tracks[track].start,
+ tracks[track].sectorSize,
+ len,
+ player.playbackRemaining / (1000 * bytesPerMs)
+ );
+ #endif
+
+ // start the channel!
+ player.channel->SetFreq(rate);
+ player.channel->Enable(true);
+ }
+ }
+ if (!is_playable) StopAudio();
+ return is_playable;
}
bool CDROM_Interface_Image::PauseAudio(bool resume)
{
- player.isPaused = !resume;
+ // Only switch states if needed
+ if (player.isPaused == resume) {
+ player.channel->Enable(resume);
+ player.isPaused = !resume;
+ }
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: PauseAudio, state=%s", get_time(), resume ? "resumed" : "paused");
+ #endif
+
return true;
}
bool CDROM_Interface_Image::StopAudio(void)
{
- player.isPlaying = false;
- player.isPaused = false;
+ // Only switch states if needed
+ if (player.isPlaying) {
+ player.channel->Enable(false);
+ player.isPlaying = false;
+ player.isPaused = false;
+ }
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: StopAudio", get_time());
+ #endif
+
return true;
}
@@ -279,7 +512,6 @@ bool CDROM_Interface_Image::ReadSectors(PhysPt buffer, bool raw, unsigned long s
MEM_BlockWrite(buffer, buf, buflen);
delete[] buf;
-
return success;
}
@@ -313,58 +545,143 @@ bool CDROM_Interface_Image::ReadSector(Bit8u *buffer, bool raw, unsigned long se
if (tracks[track].sectorSize == RAW_SECTOR_SIZE && !tracks[track].mode2 && !raw) seek += 16;
if (tracks[track].mode2 && !raw) seek += 24;
+ // LOG_MSG("CDROM: ReadSector track=%d, desired raw=%s, sector=%ld, length=%d", track, raw ? "true":"false", sector, length);
return tracks[track].file->read(buffer, seek, length);
}
+void printProgress(double percentage, const char* msg)
+{
+ // 60 is the number of characters in the full progress bar
+ int val = (int)(percentage * 100);
+ int lpad = (int)(percentage * 60);
+ int rpad = 60 - lpad;
+ LOG_MSG("\r%3d%% [%.*s%*s] - %s", val, lpad,
+ "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||", rpad, "", msg);
+ fflush(stdout);
+}
+
void CDROM_Interface_Image::CDAudioCallBack(Bitu len)
{
- len *= 4; // 16 bit, stereo
- if (!len) return;
- if (!player.isPlaying || player.isPaused) {
- player.channel->AddSilence();
- return;
- }
-
- SDL_mutexP(player.mutex);
- while (player.bufLen < (Bits)len) {
- bool success;
- if (player.targetFrame > player.currFrame)
- success = player.cd->ReadSector(&player.buffer[player.bufLen], true, player.currFrame);
- else success = false;
-
- if (success) {
- player.currFrame++;
- player.bufLen += RAW_SECTOR_SIZE;
- } else {
- memset(&player.buffer[player.bufLen], 0, len - player.bufLen);
- player.bufLen = len;
- player.isPlaying = false;
+ // Our member object "playbackRemaining" holds the
+ // exact number of stream-bytes we need to play before meeting the
+ // DOS program's desired playback duration in sectors. We simply
+ // decrement this counter each callback until we're done.
+ if (len == 0 || !player.isPlaying || player.isPaused) return;
+
+
+ // determine bytes per request (16-bit samples)
+ const Bit8u channels = player.trackFile->getChannels();
+ const Bit8u bytes_per_request = channels * 2;
+ Bit16u total_requested = len * bytes_per_request;
+
+ while (total_requested > 0) {
+ Bit16u requested = total_requested;
+
+ // Every now and then the callback wants a big number of bytes,
+ // which can exceed our circular buffer. In these cases we need
+ // read through as many iteration of our circular buffer as needed.
+ if (total_requested > AUDIO_DECODE_BUFFER_SIZE) {
+ requested = AUDIO_DECODE_BUFFER_SIZE;
+ total_requested -= AUDIO_DECODE_BUFFER_SIZE;
}
- }
- SDL_mutexV(player.mutex);
- if (player.ctrlUsed) {
- Bit16s sample0,sample1;
- Bit16s * samples=(Bit16s *)&player.buffer;
- for (Bitu pos=0;pos<len/4;pos++) {
-#if defined(WORDS_BIGENDIAN)
- sample0=(Bit16s)host_readw((HostPt)&samples[pos*2+player.ctrlData.out[0]]);
- sample1=(Bit16s)host_readw((HostPt)&samples[pos*2+player.ctrlData.out[1]]);
-#else
- sample0=samples[pos*2+player.ctrlData.out[0]];
- sample1=samples[pos*2+player.ctrlData.out[1]];
-#endif
- samples[pos*2+0]=(Bit16s)(sample0*player.ctrlData.vol[0]/255.0);
- samples[pos*2+1]=(Bit16s)(sample1*player.ctrlData.vol[1]/255.0);
+ else {
+ total_requested = 0;
}
-#if defined(WORDS_BIGENDIAN)
- player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
- } else player.channel->AddSamples_s16_nonnative(len/4,(Bit16s *)player.buffer);
-#else
+
+ // Three scenarios in order of probabilty:
+ //
+ // 1. Consume: If our decoded circular buffer is sufficiently filled to
+ // satify the requested size, then feed the callback with
+ // the requested number of bytes.
+ //
+ // 2. Wrap: If we've decoded and consumed to edge of our buffer, then
+ // we need to wrap any remaining decoded-but-not-consumed
+ // samples back around to the front of the buffer.
+ //
+ // 3. Fill: When out circular buffer is too depleted to satisfy the
+ // requested size, then perform chunked-decode reads from
+ // the audio-codec to either fill our buffer or satify our
+ // remaining playback - whichever is smaller.
+ //
+ while (true) {
+
+ // 1. Consume
+ // ==========
+ if (player.bufferPos - player.bufferConsumed >= requested) {
+ if (player.ctrlUsed) {
+ for (Bit8u i=0; i < channels; i++) {
+ Bit16s sample;
+ Bit16s* samples = (Bit16s*)&player.buffer[player.bufferConsumed];
+ for (Bitu pos = 0; pos < requested / bytes_per_request; pos++) {
+ #if defined(WORDS_BIGENDIAN)
+ sample = (Bit16s)host_readw((HostPt) & samples[pos * 2 + player.ctrlData.out[i]]);
+ #else
+ sample = samples[pos * 2 + player.ctrlData.out[i]];
+ #endif
+ samples[pos * 2 + i] = (Bit16s)(sample * player.ctrlData.vol[i] / 255.0);
+ }
+ }
+ }
+ // uses either the stereo or mono and native or nonnative AddSamples call assigned during construction
+ (player.channel->*player.addSamples)(requested / bytes_per_request, (Bit16s*)(player.buffer + player.bufferConsumed) );
+ player.bufferConsumed += requested;
+ player.playbackRemaining -= requested;
+
+ // Games can query the current Red Book MSF frame-position, so we keep that up-to-date here.
+ // We scale the final number of frames by the percent complete, which
+ // avoids having to keep track of the euivlent number of Red Book frames
+ // read (which would involve coverting the compressed streams data-rate into
+ // CDROM Red Book rate, which is more work than simply scaling).
+ //
+ const float playbackPercentSoFar = static_cast<float>(player.playbackTotal - player.playbackRemaining) / player.playbackTotal;
+ player.currFrame = player.startFrame + ceil(player.numFrames * playbackPercentSoFar);
+ break;
+ // printProgress( (player.bufferPos - player.bufferConsumed)/(float)AUDIO_DECODE_BUFFER_SIZE, "consume");
+ }
+
+ // 2. Wrap
+ // =======
+ else {
+ memcpy(player.buffer,
+ player.buffer + player.bufferConsumed,
+ player.bufferPos - player.bufferConsumed);
+ player.bufferPos -= player.bufferConsumed;
+ player.bufferConsumed = 0;
+ // printProgress( (player.bufferPos - player.bufferConsumed)/(float)AUDIO_DECODE_BUFFER_SIZE, "wrap");
+ }
+
+ // 3. Fill
+ // =======
+ const Bit16u chunkSize = player.trackFile->chunkSize;
+ while(AUDIO_DECODE_BUFFER_SIZE - player.bufferPos >= chunkSize &&
+ (player.bufferPos - player.bufferConsumed < player.playbackRemaining ||
+ player.bufferPos - player.bufferConsumed < requested) ) {
+
+ const Bit16u decoded = player.trackFile->decode(player.buffer + player.bufferPos);
+ player.bufferPos += decoded;
+
+ // if we decoded less than expected, which could be due to EOF or if the CUE file specified
+ // an exact "INDEX 01 MIN:SEC:FRAMES" value but the compressed track is ever-so-slightly less than
+ // that specified, then simply pad with zeros.
+ const Bit16s underDecode = chunkSize - decoded;
+ if (underDecode > 0) {
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: Underdecoded by %d. Feeding mixer with zeros.", get_time(), underDecode);
+ #endif
+
+ memset(player.buffer + player.bufferPos, 0, underDecode);
+ player.bufferPos += underDecode;
+ }
+ // printProgress( (player.bufferPos - player.bufferConsumed)/(float)AUDIO_DECODE_BUFFER_SIZE, "fill");
+ } // end of fill-while
+ } // end of decode and fill loop
+ } // end while total_requested
+
+ if (player.playbackRemaining <= 0) {
+ player.cd->StopAudio();
+ // printProgress( (player.bufferPos - player.bufferConsumed)/(float)AUDIO_DECODE_BUFFER_SIZE, "stop");
}
- player.channel->AddSamples_s16(len/4,(Bit16s *)player.buffer);
-#endif
- memmove(player.buffer, &player.buffer[len], player.bufLen - len);
- player.bufLen -= len;
}
bool CDROM_Interface_Image::LoadIsoFile(char* filename)
@@ -396,9 +713,14 @@ bool CDROM_Interface_Image::LoadIsoFile(char* filename)
} else if (CanReadPVD(track.file, RAW_SECTOR_SIZE, true)) {
track.sectorSize = RAW_SECTOR_SIZE;
track.mode2 = true;
- } else return false;
-
+ } else {
+ delete track.file;
+ track.file = NULL;
+ return false;
+ }
track.length = track.file->getLength() / track.sectorSize;
+ // LOG_MSG("LoadIsoFile: %s, track 1, 0x40, sectorSize=%d, mode2=%s", filename, track.sectorSize, track.mode2 ? "true":"false");
+
tracks.push_back(track);
// leadout track
@@ -408,7 +730,6 @@ bool CDROM_Interface_Image::LoadIsoFile(char* filename)
track.length = 0;
track.file = NULL;
tracks.push_back(track);
-
return true;
}
@@ -447,39 +768,39 @@ bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
int shift = 0;
int currPregap = 0;
int totalPregap = 0;
- int prestart = 0;
+ int prestart = -1;
bool success;
bool canAddTrack = false;
- char tmp[MAX_FILENAME_LENGTH]; // dirname can change its argument
+ char tmp[MAX_FILENAME_LENGTH]; // dirname can change its argument
safe_strncpy(tmp, cuefile, MAX_FILENAME_LENGTH);
string pathname(dirname(tmp));
ifstream in;
in.open(cuefile, ios::in);
if (in.fail()) return false;
-
+
while(!in.eof()) {
// get next line
char buf[MAX_LINE_LENGTH];
in.getline(buf, MAX_LINE_LENGTH);
if (in.fail() && !in.eof()) return false; // probably a binary file
istringstream line(buf);
-
+
string command;
GetCueKeyword(command, line);
-
+
if (command == "TRACK") {
if (canAddTrack) success = AddTrack(track, shift, prestart, totalPregap, currPregap);
else success = true;
-
+
track.start = 0;
track.skip = 0;
currPregap = 0;
- prestart = 0;
-
+ prestart = -1;
+
line >> track.number;
string type;
GetCueKeyword(type, line);
-
+
if (type == "AUDIO") {
track.sectorSize = RAW_SECTOR_SIZE;
track.attr = 0;
@@ -501,7 +822,7 @@ bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
track.attr = 0x40;
track.mode2 = true;
} else success = false;
-
+
canAddTrack = true;
}
else if (command == "INDEX") {
@@ -509,7 +830,7 @@ bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
line >> index;
int frame;
success = GetCueFrame(frame, line);
-
+
if (index == 1) track.start = frame;
else if (index == 0) prestart = frame;
// ignore other indices
@@ -518,7 +839,7 @@ bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
if (canAddTrack) success = AddTrack(track, shift, prestart, totalPregap, currPregap);
else success = true;
canAddTrack = false;
-
+
string filename;
GetCueString(filename, line);
GetRealFileName(filename, pathname);
@@ -530,26 +851,16 @@ bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
if (type == "BINARY") {
track.file = new BinaryFile(filename.c_str(), error);
}
-#if defined(C_SDL_SOUND)
- //The next if has been surpassed by the else, but leaving it in as not
- //to break existing cue sheets that depend on this.(mine with OGG tracks specifying MP3 as type)
- else if (type == "WAVE" || type == "AIFF" || type == "MP3") {
- track.file = new AudioFile(filename.c_str(), error);
- } else {
- const Sound_DecoderInfo **i;
- for (i = Sound_AvailableDecoders(); *i != NULL; i++) {
- if (*(*i)->extensions == type) {
- track.file = new AudioFile(filename.c_str(), error);
- break;
- }
- }
- }
-#endif
- if (error) {
- delete track.file;
- track.file = NULL;
- success = false;
- }
+ else
+ track.file = new AudioFile(filename.c_str(), error);
+ // SDL_Sound first tries using a decoder having a matching registered extension
+ // as the filename, and then falls back to trying each decoder before finally
+ // giving up.
+ if (error) {
+ delete track.file;
+ track.file = NULL;
+ success = false;
+ }
}
else if (command == "PREGAP") success = GetCueFrame(currPregap, line);
else if (command == "CATALOG") success = GetCueString(mcn, line);
@@ -558,33 +869,38 @@ bool CDROM_Interface_Image::LoadCueSheet(char *cuefile)
|| command == "PERFORMER" || command == "POSTGAP" || command == "REM"
|| command == "SONGWRITER" || command == "TITLE" || command == "") success = true;
// failure
- else success = false;
-
+ else {
+ delete track.file;
+ track.file = NULL;
+ success = false;
+ }
if (!success) return false;
}
// add last track
if (!AddTrack(track, shift, prestart, totalPregap, currPregap)) return false;
-
+
// add leadout track
track.number++;
track.attr = 0;//sync with load iso
track.start = 0;
track.length = 0;
track.file = NULL;
- if(!AddTrack(track, shift, 0, totalPregap, 0)) return false;
+ if(!AddTrack(track, shift, -1, totalPregap, 0)) return false;
return true;
}
+
+
bool CDROM_Interface_Image::AddTrack(Track &curr, int &shift, int prestart, int &totalPregap, int currPregap)
{
// frames between index 0(prestart) and 1(curr.start) must be skipped
int skip;
- if (prestart > 0) {
+ if (prestart >= 0) {
if (prestart > curr.start) return false;
skip = curr.start - prestart;
} else skip = 0;
-
+
// first track (track number must be 1)
if (tracks.empty()) {
if (curr.number != 1) return false;
@@ -594,34 +910,44 @@ bool CDROM_Interface_Image::AddTrack(Track &curr, int &shift, int prestart, int
tracks.push_back(curr);
return true;
}
-
+
Track &prev = *(tracks.end() - 1);
// current track consumes data from the same file as the previous
if (prev.file == curr.file) {
curr.start += shift;
- prev.length = curr.start + totalPregap - prev.start - skip;
- curr.skip += prev.skip + prev.length * prev.sectorSize + skip * curr.sectorSize;
+ if (!prev.length) {
+ prev.length = curr.start + totalPregap - prev.start - skip;
+ }
+ curr.skip += prev.skip + prev.length * prev.sectorSize + skip * curr.sectorSize;
totalPregap += currPregap;
curr.start += totalPregap;
// current track uses a different file as the previous track
} else {
- int tmp = prev.file->getLength() - prev.skip;
- prev.length = tmp / prev.sectorSize;
- if (tmp % prev.sectorSize != 0) prev.length++; // padding
-
+ if (!prev.length) {
+ int tmp = prev.file->getLength() - prev.skip;
+ prev.length = tmp / prev.sectorSize;
+ if (tmp % prev.sectorSize != 0) prev.length++; // padding
+ }
curr.start += prev.start + prev.length + currPregap;
curr.skip = skip * curr.sectorSize;
shift += prev.start + prev.length;
totalPregap = currPregap;
}
-
+
+ #ifdef DEBUG
+ LOG_MSG("%s CDROM: AddTrack cur.start=%d cur.len=%d cur.start+len=%d | prev.start=%d prev.len=%d prev.start+len=%d",
+ get_time(),
+ curr.start, curr.length, curr.start + curr.length,
+ prev.start, prev.length, prev.start + prev.length);
+ #endif
+
// error checks
if (curr.number <= 1) return false;
if (prev.number + 1 != curr.number) return false;
if (curr.start < prev.start + prev.length) return false;
if (curr.length < 0) return false;
-
+
tracks.push_back(curr);
return true;
}
@@ -666,7 +992,7 @@ bool CDROM_Interface_Image::GetRealFileName(string &filename, string &pathname)
#if defined (WIN32) || defined(OS2)
//Nothing
#else
- //Consider the possibility that the filename has a windows directory seperator (inside the CUE file)
+ //Consider the possibility that the filename has a windows directory seperator (inside the CUE file)
//which is common for some commercial rereleases of DOS games using DOSBox
string copy = filename;
@@ -745,14 +1071,10 @@ void CDROM_Interface_Image::ClearTracks()
}
void CDROM_Image_Destroy(Section*) {
-#if defined(C_SDL_SOUND)
Sound_Quit();
-#endif
}
-void CDROM_Image_Init(Section* section) {
-#if defined(C_SDL_SOUND)
+void CDROM_Image_Init(Section* sec) {
+ sec->AddDestroyFunction(CDROM_Image_Destroy, false);
Sound_Init();
- section->AddDestroyFunction(CDROM_Image_Destroy, false);
-#endif
}
diff --git a/src/dos/drive_iso.cpp b/src/dos/drive_iso.cpp
index 64e95ef31..9b3333d95 100644
--- a/src/dos/drive_iso.cpp
+++ b/src/dos/drive_iso.cpp
@@ -144,11 +144,11 @@ isoDrive::isoDrive(char driveLetter, const char *fileName, Bit8u mediaid, int &e
:iso(false),
dataCD(false),
mediaid(0),
+ fileName{'\0'},
subUnit(0),
- driveLetter('\0')
+ driveLetter('\0'),
+ discLabel{'\0'}
{
- this->fileName[0] = '\0';
- this->discLabel[0] = '\0';
nextFreeDirIterator = 0;
memset(dirIterators, 0, sizeof(dirIterators));
memset(sectorHashEntries, 0, sizeof(sectorHashEntries));
diff --git a/src/libs/Makefile.am b/src/libs/Makefile.am
index 303bc370e..590f502a5 100644
--- a/src/libs/Makefile.am
+++ b/src/libs/Makefile.am
@@ -1,3 +1,3 @@
AM_CPPFLAGS = -I$(top_srcdir)/include
-SUBDIRS = zmbv gui_tk
+SUBDIRS = zmbv gui_tk decoders
diff --git a/src/libs/decoders/Makefile.am b/src/libs/decoders/Makefile.am
new file mode 100644
index 000000000..7cc143b4b
--- /dev/null
+++ b/src/libs/decoders/Makefile.am
@@ -0,0 +1,58 @@
+noinst_LIBRARIES = libdecoders.a
+
+libdecoders_a_SOURCES = \
+ SDL_sound.c \
+ SDL_sound.h \
+ SDL_sound_internal.h \
+ audio_convert.c \
+ wav.c \
+ dr_wav.h \
+ flac.c \
+ dr_flac.h \
+ opus.c \
+ vorbis.c \
+ stb_vorbis.h \
+ mp3.cpp \
+ mp3_seek_table.cpp \
+ mp3_seek_table.h \
+ dr_mp3.h \
+ archive.h \
+ xxhash.c \
+ xxhash.h
+
+libdecoders_a_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ $(CPPFLAGS) \
+ -Iinternal/ogg/include \
+ -Iinternal/opus/include
+
+libdecoders_a_CXXFLAGS = \
+ $(AM_CXXFLAGS) \
+ $(CXXFLAGS) \
+ -fno-unsafe-math-optimizations \
+ -std=c++11 \
+ -Wpedantic \
+ -Wall
+
+libdecoders_a_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CFLAGS) \
+ -fno-unsafe-math-optimizations \
+ -Wpedantic \
+ -Wall
+
+opus.c: \
+ internal/include/opus/opusfile.h \
+ internal/include/speex/speex_resampler.h
+
+internal/include/opus/opusfile.h:
+ cd internal \
+ && $(MAKE) -j4 opusfile/Makefile
+
+internal/include/speex/speex_resampler.h:
+ cd internal \
+ && $(MAKE) -j4 speexdsp/Makefile
+
+clean-local:
+ cd internal \
+ && $(MAKE) dist-clean
diff --git a/src/libs/decoders/SDL_sound.c b/src/libs/decoders/SDL_sound.c
new file mode 100644
index 000000000..0b6047b89
--- /dev/null
+++ b/src/libs/decoders/SDL_sound.c
@@ -0,0 +1,833 @@
+/*
+ * SDL_sound -- An abstract sound format decoding API.
+ * Copyright (C) 2001 Ryan C. Gordon.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * This file implements the core API, which is relatively simple.
+ * The real meat of SDL_sound is in the decoders directory.
+ *
+ * Documentation is in SDL_sound.h ... It's verbose, honest. :)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon. (icculus@icculus.org)
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+// #include <stdio.h>
+// #include <stdlib.h>
+// #include <string.h>
+// #include <ctype.h>
+
+#include <SDL.h>
+#include <SDL_thread.h>
+#include "SDL_sound.h"
+
+#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_WAV;
+extern const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS;
+extern const Sound_DecoderFunctions __Sound_DecoderFunctions_OPUS;
+extern const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC;
+extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3;
+
+typedef struct
+{
+ int available;
+ const Sound_DecoderFunctions *funcs;
+} decoder_element;
+
+static decoder_element decoders[] =
+{
+ { 0, &__Sound_DecoderFunctions_WAV },
+ { 0, &__Sound_DecoderFunctions_VORBIS },
+ { 0, &__Sound_DecoderFunctions_OPUS },
+ { 0, &__Sound_DecoderFunctions_FLAC },
+ { 0, &__Sound_DecoderFunctions_MP3 },
+ { 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 **)
+ malloc((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 */
+
+ available_decoders[pos] = NULL;
+
+ 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)
+ 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;
+ 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 *) malloc(sizeof (ErrMsg));
+ if (err == NULL)
+ return; /* uhh...? */
+
+ memset((void *) err, '\0', sizeof (ErrMsg));
+ err->tid = SDL_ThreadID();
+
+ SDL_LockMutex(errorlist_mutex);
+ err->next = error_msgs;
+ error_msgs = err;
+ SDL_UnlockMutex(errorlist_mutex);
+ } /* if */
+
+ err->error_available = 1;
+ strncpy(err->error_string, str, sizeof (err->error_string));
+ err->error_string[sizeof (err->error_string) - 1] = '\0';
+} /* __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 */
+
+
+/*
+ * -ansi and -pedantic flags prevent use of strcasecmp() on Linux, and
+ * I honestly don't want to mess around with figuring out if a given
+ * platform has "strcasecmp", "stricmp", or
+ * "compare_two_damned_strings_case_insensitive", which I hear is in the
+ * next release of Carbon. :) This is exported so decoders may use it if
+ * they like.
+ */
+int __Sound_strcasecmp(const char *x, const char *y)
+{
+ int ux, uy;
+
+ if (x == y) /* same pointer? Both NULL? */
+ return(0);
+
+ if (x == NULL)
+ return(-1);
+
+ if (y == NULL)
+ return(1);
+
+ do
+ {
+ ux = toupper((int) *x);
+ uy = toupper((int) *y);
+ if (ux > uy)
+ return(1);
+ else if (ux < uy)
+ return(-1);
+ x++;
+ y++;
+ } while ((ux) && (uy));
+
+ return(0);
+} /* __Sound_strcasecmp */
+
+
+/*
+ * 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 = malloc(sizeof (Sound_Sample));
+ Sound_SampleInternal *internal = malloc(sizeof (Sound_SampleInternal));
+ if ((retval == NULL) || (internal == NULL))
+ {
+ __Sound_SetError(ERR_OUT_OF_MEMORY);
+ if (retval)
+ free(retval);
+ if (internal)
+ free(internal);
+
+ return(NULL);
+ } /* if */
+
+ memset(retval, '\0', sizeof (Sound_Sample));
+ memset(internal, '\0', sizeof (Sound_SampleInternal));
+
+ assert(bufferSize > 0);
+ retval->buffer = malloc(bufferSize); /* pure ugly. */
+ if (!retval->buffer)
+ {
+ __Sound_SetError(ERR_OUT_OF_MEMORY);
+ free(internal);
+ free(retval);
+ return(NULL);
+ } /* if */
+ memset(retval->buffer, '\0', bufferSize);
+ retval->buffer_size = bufferSize;
+
+ if (desired != NULL)
+ memcpy(&retval->desired, desired, sizeof (Sound_AudioInfo));
+
+ internal->rw = rw;
+ retval->opaque = internal;
+ return(retval);
+} /* alloc_sample */
+
+
+#if (defined DEBUG_CHATTER)
+static __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_U16MSB:
+ return("U16MSB");
+ case AUDIO_S16MSB:
+ return("S16MSB");
+ } /* 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... */
+
+ memcpy(&desired, (_desired != NULL) ? _desired : &sample->actual,
+ sizeof (Sound_AudioInfo));
+
+ if (desired.format == 0)
+ desired.format = sample->actual.format;
+ if (desired.channels == 0)
+ desired.channels = sample->actual.channels;
+ if (desired.rate == 0)
+ desired.rate = sample->actual.rate;
+
+ if (Sound_BuildAudioCVT(&internal->sdlcvt,
+ sample->actual.format,
+ sample->actual.channels,
+ sample->actual.rate,
+ desired.format,
+ desired.channels,
+ desired.rate,
+ sample->buffer_size) == -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 = 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. */
+ 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 (__Sound_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 (__Sound_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... */
+ free(retval->opaque);
+ if (retval->buffer != NULL)
+ free(retval->buffer);
+ 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 = strrchr(filename, '.');
+
+
+ SNDDBG(("Sound_NewSampleFromFile ext = `%s`", ext));
+
+ rw = SDL_RWFromFile(filename, "rb");
+ /* !!! FIXME: rw = RWops_FromFile(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_RWFromMem( (void*)data, size);
+ /* !!! FIXME: rw = RWops_FromMem(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
+ {
+ 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))
+ free(internal->buffer);
+
+ free(internal);
+
+ if (sample->buffer != NULL)
+ free(sample->buffer);
+
+ 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 = 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;
+
+ assert(sample->buffer != NULL);
+ assert(sample->buffer_size > 0);
+ assert(internal->buffer != NULL);
+ 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;
+ Sound_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 = realloc(buf, newBufSize + br);
+ if (ptr == NULL)
+ {
+ sample->flags |= SOUND_SAMPLEFLAG_ERROR;
+ __Sound_SetError(ERR_OUT_OF_MEMORY);
+ } /* if */
+ else
+ {
+ buf = ptr;
+ memcpy( ((char *) buf) + newBufSize, sample->buffer, br );
+ newBufSize += br;
+ } /* else */
+ } /* while */
+
+ if (buf == NULL) /* ...in case first call to realloc() fails... */
+ return(sample->buffer_size);
+
+ if (internal->buffer != sample->buffer)
+ free(internal->buffer);
+
+ 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/src/libs/decoders/SDL_sound.h b/src/libs/decoders/SDL_sound.h
new file mode 100644
index 000000000..b09e9ccdc
--- /dev/null
+++ b/src/libs/decoders/SDL_sound.h
@@ -0,0 +1,748 @@
+/** \file SDL_sound.h */
+
+/*
+ * SDL_sound -- An abstract sound format decoding API.
+ * Copyright (C) 2001 Ryan C. Gordon.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/**
+ * \mainpage SDL_sound
+ *
+ * The latest version of SDL_sound can be found at:
+ * http://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 http://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 MikMod and ModPlug.)
+ * - .OGG (Ogg Vorbis files, via the Vorbis libraries.)
+ * - .OPUS (Ogg Opus files, via the Opus libraries.)
+ * - .SPX (Speex files, via libspeex.)
+ * - .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>
+#include <SDL_endian.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef DOXYGEN_SHOULD_IGNORE_THIS
+
+#ifndef SDLCALL /* may not be defined with older SDL releases. */
+#define SDLCALL
+#endif
+
+#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 0
+#define SOUND_VER_PATCH 1
+#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 = 0x0, /**< No special attributes. */
+
+ /* these are set at sample creation time... */
+ SOUND_SAMPLEFLAG_CANSEEK = 0x1, /**< Sample can seek to arbitrary points. */
+
+ /* these are set during decoding... */
+ SOUND_SAMPLEFLAG_EOF = 0x2, /**< End of input stream. */
+ SOUND_SAMPLEFLAG_ERROR = 0x4, /**< Unrecoverable error. */
+ SOUND_SAMPLEFLAG_EAGAIN = 0x8 /**< 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 \<email@emailhost.dom\>" */
+ 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, 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 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_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
+}
+
+inline Sound_SampleFlags operator|(Sound_SampleFlags a, Sound_SampleFlags b)
+{return static_cast<Sound_SampleFlags>(static_cast<int>(a) | static_cast<int>(b));}
+
+inline Sound_SampleFlags& operator|= (Sound_SampleFlags& a, Sound_SampleFlags b)
+{ return (Sound_SampleFlags&)((int&)a |= static_cast<int>(b)); }
+
+inline Sound_SampleFlags operator& (Sound_SampleFlags a, Sound_SampleFlags b)
+{ return (Sound_SampleFlags)((int)a & (int)b); }
+
+inline Sound_SampleFlags& operator&= (Sound_SampleFlags& a, Sound_SampleFlags b)
+{ return (Sound_SampleFlags&)((int&)a &= (int)b); }
+#endif
+
+#endif /* !defined _INCLUDE_SDL_SOUND_H_ */
+
+/* end of SDL_sound.h ... */
diff --git a/src/libs/decoders/SDL_sound_internal.h b/src/libs/decoders/SDL_sound_internal.h
new file mode 100644
index 000000000..d1684db29
--- /dev/null
+++ b/src/libs/decoders/SDL_sound_internal.h
@@ -0,0 +1,333 @@
+/*
+ * SDL_sound -- An abstract sound format decoding API.
+ * Copyright (C) 2001 Ryan C. Gordon.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Internal function/structure declaration. Do NOT include in your
+ * application.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon. (icculus@icculus.org)
+ */
+
+#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 <SDL.h>
+
+/* SDL 1.2.4 defines this, but better safe than sorry. */
+#if (!defined(__inline__))
+# define __inline__
+#endif
+
+#if (defined DEBUG_CHATTER)
+#define SNDDBG(x) printf x
+#else
+#define SNDDBG(x)
+#endif
+
+#if HAVE_ASSERT_H
+# include <assert.h>
+#endif
+
+#ifdef _WIN32_WCE
+ extern char *strrchr(const char *s, int c);
+# ifdef NDEBUG
+# define assert(x)
+# else
+# define assert(x) if(!x) { fprintf(stderr,"Assertion failed in %s, line %s.\n",__FILE__,__LINE__); fclose(stderr); fclose(stdout); exit(1); }
+# endif
+#endif
+
+
+#if (!defined assert) /* if all else fails. */
+# define assert(x)
+#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.
+ */
+#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)
+ * Sound_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;
+
+
+/* A structure to hold a set of audio conversion filters and buffers */
+typedef struct Sound_AudioCVT
+{
+ int needed; /* Set to 1 if conversion possible */
+ Uint16 src_format; /* Source audio format */
+ Uint16 dst_format; /* Target audio format */
+ double rate_incr; /* Rate conversion increment */
+ Uint8 *buf; /* Buffer to hold entire audio data */
+ int len; /* Length of original audio buffer */
+ int len_cvt; /* Length of converted audio buffer */
+ int len_mult; /* buffer must be len*len_mult big */
+ double len_ratio; /* Given len, final size is len*len_ratio */
+ void (*filters[20])(struct Sound_AudioCVT *cvt, Uint16 *format);
+ int filter_index; /* Current audio conversion function */
+} Sound_AudioCVT;
+
+extern SNDDECLSPEC int Sound_BuildAudioCVT(Sound_AudioCVT *cvt,
+ Uint16 src_format, Uint8 src_channels, Uint32 src_rate,
+ Uint16 dst_format, Uint8 dst_channels, Uint32 dst_rate,
+ Uint32 dst_size);
+
+extern SNDDECLSPEC int Sound_ConvertAudio(Sound_AudioCVT *cvt);
+
+
+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;
+ Sound_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);
+
+/*
+ * Use this if you need a cross-platform stricmp().
+ */
+int __Sound_strcasecmp(const char *x, const char *y);
+
+
+/* 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; }
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/*--------------------------------------------------------------------------*/
+/*------------ ----------------*/
+/*------------ You MUST implement the following functions ----------------*/
+/*------------ if porting to a new platform. ----------------*/
+/*------------ (see platform/unix.c for an example) ----------------*/
+/*------------ ----------------*/
+/*--------------------------------------------------------------------------*/
+/*--------------------------------------------------------------------------*/
+
+
+/* (None, right now.) */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#endif /* defined _INCLUDE_SDL_SOUND_INTERNAL_H_ */
+
+/* end of SDL_sound_internal.h ... */
diff --git a/src/libs/decoders/archive.h b/src/libs/decoders/archive.h
new file mode 100644
index 000000000..846ceb16d
--- /dev/null
+++ b/src/libs/decoders/archive.h
@@ -0,0 +1,346 @@
+/*
+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.
+
+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.
+*/
+
+#ifndef ARCHIVE_H__
+#define ARCHIVE_H__
+
+#include <stdint.h>
+#include <string>
+#include <cassert>
+#include <algorithm>
+
+#include <vector>
+#include <deque>
+#include <list>
+#include <set>
+#include <map>
+
+#include <exception>
+#include <stdexcept>
+
+namespace EndianSwapper
+{
+ class SwapByteBase
+ {
+ public:
+ static bool ShouldSwap()
+ {
+ static const uint16_t swapTest = 1;
+ return (*((char*)&swapTest) == 1);
+ }
+
+ static void SwapBytes(uint8_t& v1, uint8_t& v2)
+ {
+ uint8_t tmp = v1;
+ v1 = v2;
+ v2 = tmp;
+ }
+ };
+
+ template <class T, int S>
+ class SwapByte : public SwapByteBase
+ {
+ public:
+ static T Swap(T v)
+ {
+ assert(false); // Shoud not be here...
+ return v;
+ }
+ };
+
+ template <class T>
+ class SwapByte<T, 1> : public SwapByteBase
+ {
+ public:
+ static T Swap(T v)
+ {
+ return v;
+ }
+ };
+
+ template <class T>
+ class SwapByte<T, 2> : public SwapByteBase
+ {
+ public:
+ static T Swap(T v)
+ {
+ if(ShouldSwap())
+ return ((uint16_t)v >> 8) | ((uint16_t)v << 8);
+ return v;
+ }
+ };
+
+ template <class T>
+ class SwapByte<T, 4> : public SwapByteBase
+ {
+ public:
+ static T Swap(T v)
+ {
+ if(ShouldSwap())
+ {
+ return (SwapByte<uint16_t, 2>::Swap((uint32_t)v & 0xffff) << 16) | (SwapByte<uint16_t, 2>::Swap(((uint32_t)v & 0xffff0000) >> 16));
+ }
+ return v;
+ }
+ };
+
+ template <class T>
+ class SwapByte<T, 8> : public SwapByteBase
+ {
+ public:
+ static T Swap(T v)
+ {
+ if(ShouldSwap())
+ return (((uint64_t)SwapByte<uint32_t, 4>::Swap((uint32_t)(v & 0xffffffffull))) << 32) | (SwapByte<uint32_t, 4>::Swap((uint32_t)(v >> 32)));
+ return v;
+ }
+ };
+
+ template <>
+ class SwapByte<float, 4> : public SwapByteBase
+ {
+ public:
+ static float Swap(float v)
+ {
+ union { float f; uint8_t c[4]; };
+ f = v;
+ if(ShouldSwap())
+ {
+ SwapBytes(c[0], c[3]);
+ SwapBytes(c[1], c[2]);
+ }
+ return f;
+ }
+ };
+
+ template <>
+ class SwapByte<double, 8> : public SwapByteBase
+ {
+ public:
+ static double Swap(double v)
+ {
+ union { double f; uint8_t c[8]; };
+ f = v;
+ if(ShouldSwap())
+ {
+ SwapBytes(c[0], c[7]);
+ SwapBytes(c[1], c[6]);
+ SwapBytes(c[2], c[5]);
+ SwapBytes(c[3], c[4]);
+ }
+ return f;
+ }
+ };
+}
+
+template <class STREAM_TYPE>
+class Archive
+{
+ public:
+ Archive(STREAM_TYPE& stream) : m_stream(stream)
+ {
+ }
+
+ public:
+ template <class T>
+ const Archive& operator<<(const T& v) const
+ {
+ *this & v;
+ return *this;
+ }
+
+ template <class T>
+ Archive& operator>>(T& v)
+ {
+ *this & v;
+ return *this;
+ }
+
+ public:
+ template <class T>
+ Archive& operator&(T& v)
+ {
+ v.Serialize(*this);
+ return *this;
+ }
+
+ template <class T>
+ const Archive& operator&(const T& v) const
+ {
+ ((T&)v).Serialize(*this);
+ return *this;
+ }
+
+ template <class T, size_t N>
+ Archive& operator&(T (&v)[N])
+ {
+ uint32_t len;
+ *this & len;
+ for(size_t i = 0; i < N; ++i)
+ *this & v[i];
+ return *this;
+ }
+
+ template <class T, size_t N>
+ const Archive& operator&(const T (&v)[N]) const
+ {
+ uint32_t len = N;
+ *this & len;
+ for(size_t i = 0; i < N; ++i)
+ *this & v[i];
+ return *this;
+ }
+
+#define SERIALIZER_FOR_POD(type) \
+ Archive& operator&(type& v) \
+ { \
+ m_stream.read((char*)&v, sizeof(type)); \
+ if(!m_stream) { throw std::runtime_error("malformed data"); } \
+ v = Swap(v); \
+ return *this; \
+ } \
+ const Archive& operator&(type v) const \
+ { \
+ v = Swap(v); \
+ m_stream.write((const char*)&v, sizeof(type)); \
+ return *this; \
+ }
+
+ SERIALIZER_FOR_POD(bool)
+ SERIALIZER_FOR_POD(char)
+ SERIALIZER_FOR_POD(unsigned char)
+ SERIALIZER_FOR_POD(short)
+ SERIALIZER_FOR_POD(unsigned short)
+ SERIALIZER_FOR_POD(int)
+ SERIALIZER_FOR_POD(unsigned int)
+ SERIALIZER_FOR_POD(long)
+ SERIALIZER_FOR_POD(unsigned long)
+ SERIALIZER_FOR_POD(long long)
+ SERIALIZER_FOR_POD(unsigned long long)
+ SERIALIZER_FOR_POD(float)
+ SERIALIZER_FOR_POD(double)
+
+
+#define SERIALIZER_FOR_STL(type) \
+ template <class T> \
+ Archive& operator&(type<T>& v) \
+ { \
+ uint32_t len; \
+ *this & len; \
+ for(uint32_t i = 0; i < len; ++i) \
+ { \
+ T value; \
+ *this & value; \
+ v.insert(v.end(), value); \
+ } \
+ return *this; \
+ } \
+ template <class T> \
+ const Archive& operator&(const type<T>& v) const \
+ { \
+ uint32_t len = v.size(); \
+ *this & len; \
+ for(typename type<T>::const_iterator it = v.begin(); it != v.end(); ++it) \
+ *this & *it; \
+ return *this; \
+ }
+
+#define SERIALIZER_FOR_STL2(type) \
+ template <class T1, class T2> \
+ Archive& operator&(type<T1, T2>& v) \
+ { \
+ uint32_t len; \
+ *this & len; \
+ for(uint32_t i = 0; i < len; ++i) \
+ { \
+ std::pair<T1, T2> value; \
+ *this & value; \
+ v.insert(v.end(), value); \
+ } \
+ return *this; \
+ } \
+ template <class T1, class T2> \
+ const Archive& operator&(const type<T1, T2>& v) const \
+ { \
+ uint32_t len = v.size(); \
+ *this & len; \
+ for(typename type<T1, T2>::const_iterator it = v.begin(); it != v.end(); ++it) \
+ *this & *it; \
+ return *this; \
+ }
+
+ SERIALIZER_FOR_STL(std::vector)
+ SERIALIZER_FOR_STL(std::deque)
+ SERIALIZER_FOR_STL(std::list)
+ SERIALIZER_FOR_STL(std::set)
+ SERIALIZER_FOR_STL(std::multiset)
+ SERIALIZER_FOR_STL2(std::map)
+ SERIALIZER_FOR_STL2(std::multimap)
+
+ template <class T1, class T2>
+ Archive& operator&(std::pair<T1, T2>& v)
+ {
+ *this & v.first & v.second;
+ return *this;
+ }
+
+ template <class T1, class T2>
+ const Archive& operator&(const std::pair<T1, T2>& v) const
+ {
+ *this & v.first & v.second;
+ return *this;
+ }
+
+ Archive& operator&(std::string& v)
+ {
+ uint32_t len;
+ *this & len;
+ v.clear();
+ char buffer[4096];
+ uint32_t toRead = len;
+ while(toRead != 0)
+ {
+ uint32_t l = std::min(toRead, (uint32_t)sizeof(buffer));
+ m_stream.read(buffer, l);
+ if(!m_stream)
+ throw std::runtime_error("malformed data");
+ v += std::string(buffer, l);
+ toRead -= l;
+ }
+ return *this;
+ }
+
+ const Archive& operator&(const std::string& v) const
+ {
+ uint32_t len = v.length();
+ *this & len;
+ m_stream.write(v.c_str(), len);
+ return *this;
+ }
+
+ private:
+ template <class T>
+ T Swap(const T& v) const
+ {
+ return EndianSwapper::SwapByte<T, sizeof(T)>::Swap(v);
+ }
+
+ private:
+ STREAM_TYPE& m_stream;
+};
+
+#endif // ARCHIVE_H__
diff --git a/src/libs/decoders/audio_convert.c b/src/libs/decoders/audio_convert.c
new file mode 100644
index 000000000..36e586635
--- /dev/null
+++ b/src/libs/decoders/audio_convert.c
@@ -0,0 +1,732 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Sam Lantinga
+ slouken@devolution.com
+*/
+
+/*
+ * This file was derived from SDL's SDL_audiocvt.c and is an attempt to
+ * address the shortcomings of it.
+ *
+ * Perhaps we can adapt some good filters from SoX?
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "SDL_sound.h"
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h"
+
+/* Functions for audio drivers to perform runtime conversion of audio format */
+
+
+/*
+ * Toggle endianness. This filter is, of course, only applied to 16-bit
+ * audio data.
+ */
+
+static void Sound_ConvertEndian(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Uint8 *data, tmp;
+
+ /* SNDDBG(("Converting audio endianness\n")); */
+
+ data = cvt->buf;
+
+ for (i = cvt->len_cvt / 2; i; --i)
+ {
+ tmp = data[0];
+ data[0] = data[1];
+ data[1] = tmp;
+ data += 2;
+ } /* for */
+
+ *format = (*format ^ 0x1000);
+} /* Sound_ConvertEndian */
+
+
+/*
+ * Toggle signed/unsigned. Apparently this is done by toggling the most
+ * significant bit of each sample.
+ */
+
+static void Sound_ConvertSign(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Uint8 *data;
+
+ /* SNDDBG(("Converting audio signedness\n")); */
+
+ data = cvt->buf;
+
+ /* 16-bit sound? */
+ if ((*format & 0xFF) == 16)
+ {
+ /* Little-endian? */
+ if ((*format & 0x1000) != 0x1000)
+ ++data;
+
+ for (i = cvt->len_cvt / 2; i; --i)
+ {
+ *data ^= 0x80;
+ data += 2;
+ } /* for */
+ } /* if */
+ else
+ {
+ for (i = cvt->len_cvt; i; --i)
+ *data++ ^= 0x80;
+ } /* else */
+
+ *format = (*format ^ 0x8000);
+} /* Sound_ConvertSign */
+
+
+/*
+ * Convert 16-bit to 8-bit. This is done by taking the most significant byte
+ * of each 16-bit sample.
+ */
+
+static void Sound_Convert8(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Uint8 *src, *dst;
+
+ /* SNDDBG(("Converting to 8-bit\n")); */
+
+ src = cvt->buf;
+ dst = cvt->buf;
+
+ /* Little-endian? */
+ if ((*format & 0x1000) != 0x1000)
+ ++src;
+
+ for (i = cvt->len_cvt / 2; i; --i)
+ {
+ *dst = *src;
+ src += 2;
+ dst += 1;
+ } /* for */
+
+ *format = ((*format & ~0x9010) | AUDIO_U8);
+ cvt->len_cvt /= 2;
+} /* Sound_Convert8 */
+
+
+/* Convert 8-bit to 16-bit - LSB */
+
+static void Sound_Convert16LSB(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Uint8 *src, *dst;
+
+ /* SNDDBG(("Converting to 16-bit LSB\n")); */
+
+ src = cvt->buf + cvt->len_cvt;
+ dst = cvt->buf + cvt->len_cvt * 2;
+
+ for (i = cvt->len_cvt; i; --i)
+ {
+ src -= 1;
+ dst -= 2;
+ dst[1] = *src;
+ dst[0] = 0;
+ } /* for */
+
+ *format = ((*format & ~0x0008) | AUDIO_U16LSB);
+ cvt->len_cvt *= 2;
+} /* Sound_Convert16LSB */
+
+
+/* Convert 8-bit to 16-bit - MSB */
+
+static void Sound_Convert16MSB(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Uint8 *src, *dst;
+
+ /* SNDDBG(("Converting to 16-bit MSB\n")); */
+
+ src = cvt->buf + cvt->len_cvt;
+ dst = cvt->buf + cvt->len_cvt * 2;
+
+ for (i = cvt->len_cvt; i; --i)
+ {
+ src -= 1;
+ dst -= 2;
+ dst[0] = *src;
+ dst[1] = 0;
+ } /* for */
+
+ *format = ((*format & ~0x0008) | AUDIO_U16MSB);
+ cvt->len_cvt *= 2;
+} /* Sound_Convert16MSB */
+
+
+/* Duplicate a mono channel to both stereo channels */
+
+static void Sound_ConvertStereo(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+
+ /* SNDDBG(("Converting to stereo\n")); */
+
+ /* 16-bit sound? */
+ if ((*format & 0xFF) == 16)
+ {
+ Uint16 *src, *dst;
+
+ src = (Uint16 *) (cvt->buf + cvt->len_cvt);
+ dst = (Uint16 *) (cvt->buf + cvt->len_cvt * 2);
+
+ for (i = cvt->len_cvt/2; i; --i)
+ {
+ dst -= 2;
+ src -= 1;
+ dst[0] = src[0];
+ dst[1] = src[0];
+ } /* for */
+ } /* if */
+ else
+ {
+ Uint8 *src, *dst;
+
+ src = cvt->buf + cvt->len_cvt;
+ dst = cvt->buf + cvt->len_cvt * 2;
+
+ for (i = cvt->len_cvt; i; --i)
+ {
+ dst -= 2;
+ src -= 1;
+ dst[0] = src[0];
+ dst[1] = src[0];
+ } /* for */
+ } /* else */
+
+ cvt->len_cvt *= 2;
+} /* Sound_ConvertStereo */
+
+
+/* Effectively mix right and left channels into a single channel */
+
+static void Sound_ConvertMono(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Sint32 sample;
+ Uint8 *u_src, *u_dst;
+ Sint8 *s_src, *s_dst;
+
+ /* SNDDBG(("Converting to mono\n")); */
+
+ switch (*format)
+ {
+ case AUDIO_U8:
+ u_src = cvt->buf;
+ u_dst = cvt->buf;
+
+ for (i = cvt->len_cvt / 2; i; --i)
+ {
+ sample = u_src[0] + u_src[1];
+ *u_dst = (sample > 255) ? 255 : sample;
+ u_src += 2;
+ u_dst += 1;
+ } /* for */
+ break;
+
+ case AUDIO_S8:
+ s_src = (Sint8 *) cvt->buf;
+ s_dst = (Sint8 *) cvt->buf;
+
+ for (i = cvt->len_cvt / 2; i; --i)
+ {
+ sample = s_src[0] + s_src[1];
+ if (sample > 127)
+ *s_dst = 127;
+ else if (sample < -128)
+ *s_dst = -128;
+ else
+ *s_dst = sample;
+
+ s_src += 2;
+ s_dst += 1;
+ } /* for */
+ break;
+
+ case AUDIO_U16MSB:
+ u_src = cvt->buf;
+ u_dst = cvt->buf;
+
+ for (i = cvt->len_cvt / 4; i; --i)
+ {
+ sample = (Uint16) ((u_src[0] << 8) | u_src[1])
+ + (Uint16) ((u_src[2] << 8) | u_src[3]);
+ if (sample > 65535)
+ {
+ u_dst[0] = 0xFF;
+ u_dst[1] = 0xFF;
+ } /* if */
+ else
+ {
+ u_dst[1] = (sample & 0xFF);
+ sample >>= 8;
+ u_dst[0] = (sample & 0xFF);
+ } /* else */
+ u_src += 4;
+ u_dst += 2;
+ } /* for */
+ break;
+
+ case AUDIO_U16LSB:
+ u_src = cvt->buf;
+ u_dst = cvt->buf;
+
+ for (i = cvt->len_cvt / 4; i; --i)
+ {
+ sample = (Uint16) ((u_src[1] << 8) | u_src[0])
+ + (Uint16) ((u_src[3] << 8) | u_src[2]);
+ if (sample > 65535)
+ {
+ u_dst[0] = 0xFF;
+ u_dst[1] = 0xFF;
+ } /* if */
+ else
+ {
+ u_dst[0] = (sample & 0xFF);
+ sample >>= 8;
+ u_dst[1] = (sample & 0xFF);
+ } /* else */
+ u_src += 4;
+ u_dst += 2;
+ } /* for */
+ break;
+
+ case AUDIO_S16MSB:
+ u_src = cvt->buf;
+ u_dst = cvt->buf;
+
+ for (i = cvt->len_cvt / 4; i; --i)
+ {
+ sample = (Sint16) ((u_src[0] << 8) | u_src[1])
+ + (Sint16) ((u_src[2] << 8) | u_src[3]);
+ if (sample > 32767)
+ {
+ u_dst[0] = 0x7F;
+ u_dst[1] = 0xFF;
+ } /* if */
+ else if (sample < -32768)
+ {
+ u_dst[0] = 0x80;
+ u_dst[1] = 0x00;
+ } /* else if */
+ else
+ {
+ u_dst[1] = (sample & 0xFF);
+ sample >>= 8;
+ u_dst[0] = (sample & 0xFF);
+ } /* else */
+ u_src += 4;
+ u_dst += 2;
+ } /* for */
+ break;
+
+ case AUDIO_S16LSB:
+ u_src = cvt->buf;
+ u_dst = cvt->buf;
+
+ for (i = cvt->len_cvt / 4; i; --i)
+ {
+ sample = (Sint16) ((u_src[1] << 8) | u_src[0])
+ + (Sint16) ((u_src[3] << 8) | u_src[2]);
+ if (sample > 32767)
+ {
+ u_dst[1] = 0x7F;
+ u_dst[0] = 0xFF;
+ } /* if */
+ else if (sample < -32768)
+ {
+ u_dst[1] = 0x80;
+ u_dst[0] = 0x00;
+ } /* else if */
+ else
+ {
+ u_dst[0] = (sample & 0xFF);
+ sample >>= 8;
+ u_dst[1] = (sample & 0xFF);
+ } /* else */
+ u_src += 4;
+ u_dst += 2;
+ } /* for */
+ break;
+ } /* switch */
+
+ cvt->len_cvt /= 2;
+} /* Sound_ConvertMono */
+
+
+/* Convert rate up by multiple of 2 */
+
+static void Sound_RateMUL2(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Uint8 *src, *dst;
+
+ /* SNDDBG(("Converting audio rate * 2\n")); */
+
+ src = cvt->buf + cvt->len_cvt;
+ dst = cvt->buf + cvt->len_cvt*2;
+
+ /* 8- or 16-bit sound? */
+ switch (*format & 0xFF)
+ {
+ case 8:
+ for (i = cvt->len_cvt; i; --i)
+ {
+ src -= 1;
+ dst -= 2;
+ dst[0] = src[0];
+ dst[1] = src[0];
+ } /* for */
+ break;
+
+ case 16:
+ for (i = cvt->len_cvt / 2; i; --i)
+ {
+ src -= 2;
+ dst -= 4;
+ dst[0] = src[0];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ dst[3] = src[1];
+ } /* for */
+ break;
+ } /* switch */
+
+ cvt->len_cvt *= 2;
+} /* Sound_RateMUL2 */
+
+
+/* Convert rate down by multiple of 2 */
+
+static void Sound_RateDIV2(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ int i;
+ Uint8 *src, *dst;
+
+ /* SNDDBG(("Converting audio rate / 2\n")); */
+
+ src = cvt->buf;
+ dst = cvt->buf;
+
+ /* 8- or 16-bit sound? */
+ switch (*format & 0xFF)
+ {
+ case 8:
+ for (i = cvt->len_cvt / 2; i; --i)
+ {
+ dst[0] = src[0];
+ src += 2;
+ dst += 1;
+ } /* for */
+ break;
+
+ case 16:
+ for (i = cvt->len_cvt / 4; i; --i)
+ {
+ dst[0] = src[0];
+ dst[1] = src[1];
+ src += 4;
+ dst += 2;
+ }
+ break;
+ } /* switch */
+
+ cvt->len_cvt /= 2;
+} /* Sound_RateDIV2 */
+
+
+/* Very slow rate conversion routine */
+
+static void Sound_RateSLOW(Sound_AudioCVT *cvt, Uint16 *format)
+{
+ double ipos;
+ int i, clen;
+ Uint8 *output8;
+ Uint16 *output16;
+
+ /* SNDDBG(("Converting audio rate * %4.4f\n", 1.0/cvt->rate_incr)); */
+
+ clen = (int) ((double) cvt->len_cvt / cvt->rate_incr);
+
+ if (cvt->rate_incr > 1.0)
+ {
+ /* 8- or 16-bit sound? */
+ switch (*format & 0xFF)
+ {
+ case 8:
+ output8 = cvt->buf;
+
+ ipos = 0.0;
+ for (i = clen; i; --i)
+ {
+ *output8 = cvt->buf[(int) ipos];
+ ipos += cvt->rate_incr;
+ output8 += 1;
+ } /* for */
+ break;
+
+ case 16:
+ output16 = (Uint16 *) cvt->buf;
+
+ clen &= ~1;
+ ipos = 0.0;
+ for (i = clen / 2; i; --i)
+ {
+ *output16 = ((Uint16 *) cvt->buf)[(int) ipos];
+ ipos += cvt->rate_incr;
+ output16 += 1;
+ } /* for */
+ break;
+ } /* switch */
+ } /* if */
+ else
+ {
+ /* 8- or 16-bit sound */
+ switch (*format & 0xFF)
+ {
+ case 8:
+ output8 = cvt->buf + clen;
+
+ ipos = (double) cvt->len_cvt;
+ for (i = clen; i; --i)
+ {
+ ipos -= cvt->rate_incr;
+ output8 -= 1;
+ *output8 = cvt->buf[(int) ipos];
+ } /* for */
+ break;
+
+ case 16:
+ clen &= ~1;
+ output16 = (Uint16 *) (cvt->buf + clen);
+ ipos = (double) cvt->len_cvt / 2;
+ for (i = clen / 2; i; --i)
+ {
+ ipos -= cvt->rate_incr;
+ output16 -= 1;
+ *output16 = ((Uint16 *) cvt->buf)[(int) ipos];
+ } /* for */
+ break;
+ } /* switch */
+ } /* else */
+
+ cvt->len_cvt = clen;
+} /* Sound_RateSLOW */
+
+
+int Sound_ConvertAudio(Sound_AudioCVT *cvt)
+{
+ Uint16 format;
+
+ /* Make sure there's data to convert */
+ if (cvt->buf == NULL)
+ {
+ __Sound_SetError("No buffer allocated for conversion");
+ return(-1);
+ } /* if */
+
+ /* Return okay if no conversion is necessary */
+ cvt->len_cvt = cvt->len;
+ if (cvt->filters[0] == NULL)
+ return(0);
+
+ /* Set up the conversion and go! */
+ format = cvt->src_format;
+ for (cvt->filter_index = 0; cvt->filters[cvt->filter_index];
+ cvt->filter_index++)
+ {
+ cvt->filters[cvt->filter_index](cvt, &format);
+ }
+ return(0);
+} /* Sound_ConvertAudio */
+
+
+/*
+ * Creates a set of audio filters to convert from one format to another.
+ * Returns -1 if the format conversion is not supported, or 1 if the
+ * audio filter is set up.
+ */
+
+int Sound_BuildAudioCVT(Sound_AudioCVT *cvt,
+ Uint16 src_format, Uint8 src_channels, Uint32 src_rate,
+ Uint16 dst_format, Uint8 dst_channels, Uint32 dst_rate,
+ Uint32 dst_size)
+{
+ /* Start off with no conversion necessary */
+ cvt->needed = 0;
+ cvt->filter_index = 0;
+ cvt->filters[0] = NULL;
+ cvt->len_mult = 1;
+ cvt->len_ratio = 1.0;
+
+ /* First filter: Endian conversion from src to dst */
+ if ((src_format & 0x1000) != (dst_format & 0x1000) &&
+ ((src_format & 0xff) != 8))
+ {
+ SNDDBG(("Adding filter: Sound_ConvertEndian\n"));
+ cvt->filters[cvt->filter_index++] = Sound_ConvertEndian;
+ } /* if */
+
+ /* Second filter: Sign conversion -- signed/unsigned */
+ if ((src_format & 0x8000) != (dst_format & 0x8000))
+ {
+ SNDDBG(("Adding filter: Sound_ConvertSign\n"));
+ cvt->filters[cvt->filter_index++] = Sound_ConvertSign;
+ } /* if */
+
+ /* Next filter: Convert 16 bit <--> 8 bit PCM. */
+ if ((src_format & 0xFF) != (dst_format & 0xFF))
+ {
+ switch (dst_format & 0x10FF)
+ {
+ case AUDIO_U8:
+ SNDDBG(("Adding filter: Sound_Convert8\n"));
+ cvt->filters[cvt->filter_index++] = Sound_Convert8;
+ cvt->len_ratio /= 2;
+ break;
+
+ case AUDIO_U16LSB:
+ SNDDBG(("Adding filter: Sound_Convert16LSB\n"));
+ cvt->filters[cvt->filter_index++] = Sound_Convert16LSB;
+ cvt->len_mult *= 2;
+ cvt->len_ratio *= 2;
+ break;
+
+ case AUDIO_U16MSB:
+ SNDDBG(("Adding filter: Sound_Convert16MSB\n"));
+ cvt->filters[cvt->filter_index++] = Sound_Convert16MSB;
+ cvt->len_mult *= 2;
+ cvt->len_ratio *= 2;
+ break;
+ } /* switch */
+ } /* if */
+
+ /* Next filter: Mono/Stereo conversion */
+ if (src_channels != dst_channels)
+ {
+ while ((src_channels * 2) <= dst_channels)
+ {
+ SNDDBG(("Adding filter: Sound_ConvertStereo\n"));
+ cvt->filters[cvt->filter_index++] = Sound_ConvertStereo;
+ cvt->len_mult *= 2;
+ src_channels *= 2;
+ cvt->len_ratio *= 2;
+ } /* while */
+
+ /* This assumes that 4 channel audio is in the format:
+ * Left {front/back} + Right {front/back}
+ * so converting to L/R stereo works properly.
+ */
+ while (((src_channels % 2) == 0) &&
+ ((src_channels / 2) >= dst_channels))
+ {
+ SNDDBG(("Adding filter: Sound_ConvertMono\n"));
+ cvt->filters[cvt->filter_index++] = Sound_ConvertMono;
+ src_channels /= 2;
+ cvt->len_ratio /= 2;
+ } /* while */
+
+ if ( src_channels != dst_channels ) {
+ /* Uh oh.. */;
+ } /* if */
+ } /* if */
+
+ /* Do rate conversion */
+ cvt->rate_incr = 0.0;
+ if ((src_rate / 100) != (dst_rate / 100))
+ {
+ Uint32 hi_rate, lo_rate;
+ int len_mult;
+ double len_ratio;
+ void (*rate_cvt)(Sound_AudioCVT *cvt, Uint16 *format);
+
+ if (src_rate > dst_rate)
+ {
+ hi_rate = src_rate;
+ lo_rate = dst_rate;
+ SNDDBG(("Adding filter: Sound_RateDIV2\n"));
+ rate_cvt = Sound_RateDIV2;
+ len_mult = 1;
+ len_ratio = 0.5;
+ } /* if */
+ else
+ {
+ hi_rate = dst_rate;
+ lo_rate = src_rate;
+ SNDDBG(("Adding filter: Sound_RateMUL2\n"));
+ rate_cvt = Sound_RateMUL2;
+ len_mult = 2;
+ len_ratio = 2.0;
+ } /* else */
+
+ /* If hi_rate = lo_rate*2^x then conversion is easy */
+ while (((lo_rate * 2) / 100) <= (hi_rate / 100))
+ {
+ cvt->filters[cvt->filter_index++] = rate_cvt;
+ cvt->len_mult *= len_mult;
+ lo_rate *= 2;
+ cvt->len_ratio *= len_ratio;
+ } /* while */
+
+ /* We may need a slow conversion here to finish up */
+ if ((lo_rate / 100) != (hi_rate / 100))
+ {
+ if (src_rate < dst_rate)
+ {
+ cvt->rate_incr = (double) lo_rate / hi_rate;
+ cvt->len_mult *= 2;
+ cvt->len_ratio /= cvt->rate_incr;
+ } /* if */
+ else
+ {
+ cvt->rate_incr = (double) hi_rate / lo_rate;
+ cvt->len_ratio *= cvt->rate_incr;
+ } /* else */
+ SNDDBG(("Adding filter: Sound_RateSLOW\n"));
+ cvt->filters[cvt->filter_index++] = Sound_RateSLOW;
+ } /* if */
+ } /* if */
+
+ /* Set up the filter information */
+ if (cvt->filter_index != 0)
+ {
+ cvt->needed = 1;
+ cvt->src_format = src_format;
+ cvt->dst_format = dst_format;
+ cvt->len = 0;
+ cvt->buf = NULL;
+ cvt->filters[cvt->filter_index] = NULL;
+ } /* if */
+
+ return(cvt->needed);
+} /* Sound_BuildAudioCVT */
+
+/* end of audio_convert.c ... */
diff --git a/src/libs/decoders/docs/copying.txt b/src/libs/decoders/docs/copying.txt
new file mode 100644
index 000000000..e057ccdd3
--- /dev/null
+++ b/src/libs/decoders/docs/copying.txt
@@ -0,0 +1,438 @@
+Other external libraries (such as Ogg Vorbis, SMPEG, etc) have their own
+ licenses which you should be aware of before including the related code
+ in your configuration. Most (if not all) are also under the LGPL, but are
+ external projects and we've got no control over them.
+If you want to use SDL_sound under a closed-source license, please contact
+ Ryan (icculus@icculus.org), and we can discuss an alternate license for
+ money to be distributed between the contributors to this work, but I'd
+ encourage you to abide by the LGPL, since the usual concern is whether you
+ can use this library without releasing your own source code (you can).
+-------------------
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+ Preamble
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+ a) The modified work must itself be a software library.
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+ NO WARRANTY
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+Also add information on how to contact you by electronic and paper mail.
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+That's all there is to it!
diff --git a/src/libs/decoders/docs/credits.txt b/src/libs/decoders/docs/credits.txt
new file mode 100644
index 000000000..1a490cee5
--- /dev/null
+++ b/src/libs/decoders/docs/credits.txt
@@ -0,0 +1,66 @@
+ ----------------------
+ | SDL_sound credits. |
+ ----------------------
+
+Initial API interface and implementation,
+RAW driver,
+VOC driver,
+MPG123 driver,
+WAV driver,
+OGG driver,
+SHN driver,
+Unix support,
+BeOS support:
+ Ryan C. Gordon
+
+Bug fixes,
+FreeBSD testing:
+ Tsuyoshi Iguchi
+
+Code cleanups and fixes,
+AIFF driver,
+MikMod driver,
+MIDI driver,
+ModPlug driver,
+FLAC driver:
+ Torbjörn Andersson
+
+autoconf,
+MacOS X support:
+ Max Horn
+
+win32 support,
+PocketPC support,
+other fixes:
+ Tyler Montbriand
+
+AU driver,
+ Mattias Engdegård
+
+MacOS Classic support,
+quicktime decoder,
+OS X fixes:
+ Darrell Walisser
+
+Alternate audio conversion code:
+ Frank Ranostaj
+
+Initial Borland C++ project files:
+ Dominique Louis
+
+Bugfixes and stuff:
+ Eric Wing
+
+FLAC 1.1.3 updates:
+ Josh Coalson
+
+Fixes:
+ Chris Nelson
+
+Fixes:
+ Ozkan Sezer
+
+Other stuff:
+ Your name here! Patches go to icculus@icculus.org ...
+
+/* end of CREDITS ... */
diff --git a/src/libs/decoders/docs/license.txt b/src/libs/decoders/docs/license.txt
new file mode 100644
index 000000000..4fc0a55b9
--- /dev/null
+++ b/src/libs/decoders/docs/license.txt
@@ -0,0 +1,526 @@
+Please note that the included source from Timidity, the MIDI decoder, is also
+ licensed under the following terms (GNU LGPL), but can also be used
+ separately under the GNU GPL, or the Perl Artistic License. Those licensing
+ terms are not reprinted here, but can be found on the web easily.
+
+The included source for libmpg123, the MP3 decoder, is also licensed under the
+ following terms (GNU LGPL). We make no promises about patents you might
+ violate or royalty payments you might have to pay with this or any decoder.
+
+Other external libraries (such as Ogg Vorbis, mikmod, etc) have their own
+ licenses which you should be aware of before including the related code
+ in your configuration. Most (if not all) are also under the LGPL, but are
+ external projects and we've got no control over them.
+
+If you want to use SDL_sound under a closed-source license, please contact
+ Ryan (icculus@icculus.org), and we can discuss an alternate license for
+ money to be distributed between the contributors to this work, but I'd
+ encourage you to abide by the LGPL, since the usual concern is whether you
+ can use this library without releasing your own source code (you can).
+
+
+-------------------
+
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/src/libs/decoders/dr_flac.h b/src/libs/decoders/dr_flac.h
new file mode 100644
index 000000000..972f4d54a
--- /dev/null
+++ b/src/libs/decoders/dr_flac.h
@@ -0,0 +1,11032 @@
+/*
+FLAC audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
+dr_flac - v0.12.2 - 2019-10-07
+
+David Reid - mackron@gmail.com
+*/
+
+/*
+RELEASE NOTES - v0.12.0
+=======================
+Version 0.12.0 has breaking API changes including changes to the existing API and the removal of deprecated APIs.
+
+
+Improved Client-Defined Memory Allocation
+-----------------------------------------
+The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
+existing system of DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE are still in place and will be used by default when no custom
+allocation callbacks are specified.
+
+To use the new system, you pass in a pointer to a drflac_allocation_callbacks object to drflac_open() and family, like this:
+
+ void* my_malloc(size_t sz, void* pUserData)
+ {
+ return malloc(sz);
+ }
+ void* my_realloc(void* p, size_t sz, void* pUserData)
+ {
+ return realloc(p, sz);
+ }
+ void my_free(void* p, void* pUserData)
+ {
+ free(p);
+ }
+
+ ...
+
+ drflac_allocation_callbacks allocationCallbacks;
+ allocationCallbacks.pUserData = &myData;
+ allocationCallbacks.onMalloc = my_malloc;
+ allocationCallbacks.onRealloc = my_realloc;
+ allocationCallbacks.onFree = my_free;
+ drflac* pFlac = drflac_open_file("my_file.flac", &allocationCallbacks);
+
+The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
+
+Passing in null for the allocation callbacks object will cause dr_flac to use defaults which is the same as DRFLAC_MALLOC,
+DRFLAC_REALLOC and DRFLAC_FREE and the equivalent of how it worked in previous versions.
+
+Every API that opens a drflac object now takes this extra parameter. These include the following:
+
+ drflac_open()
+ drflac_open_relaxed()
+ drflac_open_with_metadata()
+ drflac_open_with_metadata_relaxed()
+ drflac_open_file()
+ drflac_open_file_with_metadata()
+ drflac_open_memory()
+ drflac_open_memory_with_metadata()
+ drflac_open_and_read_pcm_frames_s32()
+ drflac_open_and_read_pcm_frames_s16()
+ drflac_open_and_read_pcm_frames_f32()
+ drflac_open_file_and_read_pcm_frames_s32()
+ drflac_open_file_and_read_pcm_frames_s16()
+ drflac_open_file_and_read_pcm_frames_f32()
+ drflac_open_memory_and_read_pcm_frames_s32()
+ drflac_open_memory_and_read_pcm_frames_s16()
+ drflac_open_memory_and_read_pcm_frames_f32()
+
+
+
+Optimizations
+-------------
+Seeking performance has been greatly improved. A new binary search based seeking algorithm has been introduced which significantly
+improves performance over the brute force method which was used when no seek table was present. Seek table based seeking also takes
+advantage of the new binary search seeking system to further improve performance there as well. Note that this depends on CRC which
+means it will be disabled when DR_FLAC_NO_CRC is used.
+
+The SSE4.1 pipeline has been cleaned up and optimized. You should see some improvements with decoding speed of 24-bit files in
+particular. 16-bit streams should also see some improvement.
+
+drflac_read_pcm_frames_s16() has been optimized. Previously this sat on top of drflac_read_pcm_frames_s32() and performed it's s32
+to s16 conversion in a second pass. This is now all done in a single pass. This includes SSE2 and ARM NEON optimized paths.
+
+A minor optimization has been implemented for drflac_read_pcm_frames_s32(). This will now use an SSE2 optimized pipeline for stereo
+channel reconstruction which is the last part of the decoding process.
+
+The ARM build has seen a few improvements. The CLZ (count leading zeroes) and REV (byte swap) instructions are now used when
+compiling with GCC and Clang which is achieved using inline assembly. The CLZ instruction requires ARM architecture version 5 at
+compile time and the REV instruction requires ARM architecture version 6.
+
+An ARM NEON optimized pipeline has been implemented. To enable this you'll need to add -mfpu=neon to the command line when compiling.
+
+
+Removed APIs
+------------
+The following APIs were deprecated in version 0.11.0 and have been completely removed in version 0.12.0:
+
+ drflac_read_s32() -> drflac_read_pcm_frames_s32()
+ drflac_read_s16() -> drflac_read_pcm_frames_s16()
+ drflac_read_f32() -> drflac_read_pcm_frames_f32()
+ drflac_seek_to_sample() -> drflac_seek_to_pcm_frame()
+ drflac_open_and_decode_s32() -> drflac_open_and_read_pcm_frames_s32()
+ drflac_open_and_decode_s16() -> drflac_open_and_read_pcm_frames_s16()
+ drflac_open_and_decode_f32() -> drflac_open_and_read_pcm_frames_f32()
+ drflac_open_and_decode_file_s32() -> drflac_open_file_and_read_pcm_frames_s32()
+ drflac_open_and_decode_file_s16() -> drflac_open_file_and_read_pcm_frames_s16()
+ drflac_open_and_decode_file_f32() -> drflac_open_file_and_read_pcm_frames_f32()
+ drflac_open_and_decode_memory_s32() -> drflac_open_memory_and_read_pcm_frames_s32()
+ drflac_open_and_decode_memory_s16() -> drflac_open_memory_and_read_pcm_frames_s16()
+ drflac_open_and_decode_memory_f32() -> drflac_open_memroy_and_read_pcm_frames_f32()
+
+Prior versions of dr_flac operated on a per-sample basis whereas now it operates on PCM frames. The removed APIs all relate
+to the old per-sample APIs. You now need to use the "pcm_frame" versions.
+*/
+
+
+/*
+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", NULL);
+ if (pFlac == NULL) {
+ // Failed to open FLAC file
+ }
+
+ drflac_int32* pSamples = malloc(pFlac->totalPCMFrameCount * pFlac->channels * sizeof(drflac_int32));
+ drflac_uint64 numberOfInterleavedSamplesActuallyRead = drflac_read_pcm_frames_s32(pFlac, pFlac->totalPCMFrameCount, 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_pcm_frames_s32(pFlac, chunkSizeInPCMFrames, 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 totalPCMFrameCount;
+ drflac_int32* pSampleData = drflac_open_file_and_read_pcm_frames_s32("MySong.flac", &channels, &sampleRate, &totalPCMFrameCount, NULL);
+ 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 or 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
+corrupt 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() and family.
+
+#define DR_FLAC_NO_OGG
+ Disables support for Ogg/FLAC streams.
+
+#define DR_FLAC_BUFFER_SIZE <number>
+ 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. This will disable binary search seeking.
+ When seeking, the seek table will be used if available. Otherwise the seek will be performed using brute force.
+
+#define DR_FLAC_NO_SIMD
+ Disables SIMD optimizations (SSE on x86/x64 architectures, NEON on ARM 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.
+- This has not been tested on big-endian architectures.
+- 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 <stddef.h>
+
+#if 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 <stdint.h>
+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
+
+#if defined(_MSC_VER) && _MSC_VER >= 1700 /* Visual Studio 2012 */
+ #define DRFLAC_DEPRECATED __declspec(deprecated)
+#elif (defined(__GNUC__) && __GNUC__ >= 4) /* GCC 4 */
+ #define DRFLAC_DEPRECATED __attribute__((deprecated))
+#elif defined(__has_feature) /* Clang */
+ #if __has_feature(attribute_deprecated)
+ #define DRFLAC_DEPRECATED __attribute__((deprecated))
+ #else
+ #define DRFLAC_DEPRECATED
+ #endif
+#else
+ #define DRFLAC_DEPRECATED
+#endif
+
+/*
+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) || defined(_LP64) || defined(__LP64__)
+#define DRFLAC_64BIT
+#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 firstPCMFrame;
+ drflac_uint64 flacFrameOffset; /* The offset from the first byte of the header of the first frame. */
+ drflac_uint16 pcmFrameCount;
+} drflac_seekpoint;
+#pragma pack()
+
+typedef struct
+{
+ drflac_uint16 minBlockSizeInPCMFrames;
+ drflac_uint16 maxBlockSizeInPCMFrames;
+ drflac_uint32 minFrameSizeInPCMFrames;
+ drflac_uint32 maxFrameSizeInPCMFrames;
+ drflac_uint32 sampleRate;
+ drflac_uint8 channels;
+ drflac_uint8 bitsPerSample;
+ drflac_uint64 totalPCMFrameCount;
+ 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 void* pComments;
+ } vorbis_comment;
+
+ struct
+ {
+ char catalog[128];
+ drflac_uint64 leadInSampleCount;
+ drflac_bool32 isCD;
+ drflac_uint8 trackCount;
+ const void* 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.
+
+When seeking to a PCM frame using drflac_seek_to_pcm_frame(), dr_flac may call this with an offset beyond the end of
+the FLAC stream. This needs to be detected and handled by returning DRFLAC_FALSE.
+*/
+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);
+
+
+typedef struct
+{
+ void* pUserData;
+ void* (* onMalloc)(size_t sz, void* pUserData);
+ void* (* onRealloc)(void* p, size_t sz, void* pUserData);
+ void (* onFree)(void* p, void* pUserData);
+} drflac_allocation_callbacks;
+
+/* 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;
+
+ /* A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData. */
+ drflac_int32* pSamplesS32;
+} drflac_subframe;
+
+typedef struct
+{
+ /*
+ If the stream uses variable block sizes, this will be set to the index of the first PCM frame. If fixed block sizes are used, this will
+ always be set to 0.
+ */
+ drflac_uint64 pcmFrameNumber;
+
+ /* 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 flacFrameNumber;
+
+ /* The sample rate of this frame. */
+ drflac_uint32 sampleRate;
+
+ /* The number of PCM frames in each sub-frame within this frame. */
+ drflac_uint16 blockSizeInPCMFrames;
+
+ /*
+ 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 PCM frames left to be read in this FLAC frame. This is initially set to the block size. As PCM frames 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 pcmFramesRemaining;
+
+ /* 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;
+
+ /* Memory allocation callbacks. */
+ drflac_allocation_callbacks allocationCallbacks;
+
+
+ /* 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 maxBlockSizeInPCMFrames;
+
+ /*
+ The total number of PCM Frames making up the stream. Can be 0 in which case it's still a valid stream, but just means
+ the total PCM frame count is unknown. Likely the case with streams like internet radio.
+ */
+ drflac_uint64 totalPCMFrameCount;
+
+
+ /* 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 currentFLACFrame;
+
+
+ /* The index of the PCM frame the decoder is currently sitting on. This is only used for seeking. */
+ drflac_uint64 currentPCMFrame;
+
+ /* The position of the first FLAC frame in the stream. This is only ever used for seeking. */
+ drflac_uint64 firstFLACFramePosInBytes;
+
+
+ /* 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;
+
+ /* Internal use only. Used for profiling and testing different seeking modes. */
+ drflac_bool32 _noSeekTableSeek : 1;
+ drflac_bool32 _noBinarySearchSeek : 1;
+ drflac_bool32 _noBruteForceSeek : 1;
+
+ /* 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.
+pAllocationCallbacks [in, optional] A pointer to application defined callbacks for managing memory allocations.
+
+Returns a pointer to an object representing the decoder.
+
+Close the decoder with drflac_close().
+
+pAllocationCallbacks can be NULL in which case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE.
+
+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, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/*
+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, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/*
+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.
+pAllocationCallbacks [in, optional] A pointer to application defined callbacks for managing memory allocations.
+
+Returns a pointer to an object representing the decoder.
+
+Close the decoder with drflac_close().
+
+pAllocationCallbacks can be NULL in which case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE.
+
+This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will allocate and free
+memory on the heap 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, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/*
+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, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/*
+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.
+framesToRead [in] The number of PCM frames to read.
+pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples.
+
+Returns the number of PCM frames 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 frames
+seeked.
+*/
+drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut);
+
+/*
+Same as drflac_read_pcm_frames_s32(), except outputs samples as 16-bit integer PCM rather than 32-bit.
+
+Note that this is lossy for streams where the bits per sample is larger than 16.
+*/
+drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut);
+
+/*
+Same as drflac_read_pcm_frames_s32(), except outputs samples as 32-bit floating-point PCM.
+
+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_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut);
+
+/*
+Seeks to the PCM frame at the given index.
+
+pFlac [in] The decoder.
+pcmFrameIndex [in] The index of the PCM frame to seek to. See notes below.
+
+Returns DRFLAC_TRUE if successful; DRFLAC_FALSE otherwise.
+*/
+drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex);
+
+
+
+#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.
+pAllocationCallbacks [in, optional] A pointer to application defined callbacks for managing memory allocations.
+
+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, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/*
+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, const drflac_allocation_callbacks* pAllocationCallbacks);
+#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, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/*
+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, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+
+
+/* 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().
+
+You can pass in custom memory allocation callbacks via the pAllocationCallbacks parameter. This can be NULL in which
+case it will use DRFLAC_MALLOC, DRFLAC_REALLOC and 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_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/* Same as drflac_open_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */
+drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/* Same as drflac_open_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */
+float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+#ifndef DR_FLAC_NO_STDIO
+/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a file. */
+drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */
+drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/* Same as drflac_open_file_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */
+float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+#endif
+
+/* Same as drflac_open_and_read_pcm_frames_s32() except opens the decoder from a block of memory. */
+drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns signed 16-bit integer samples. */
+drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/* Same as drflac_open_memory_and_read_pcm_frames_s32(), except returns 32-bit floating-point samples. */
+float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+/*
+Frees memory that was allocated internally by dr_flac.
+
+Set pAllocationCallbacks to the same object that was passed to drflac_open_*_and_read_pcm_frames_*(). If you originally passed in NULL, pass in NULL for this.
+*/
+void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks);
+
+
+/* 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 void* 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);
+
+
+/* Structure representing an iterator for cuesheet tracks in a CUESHEET metadata block. */
+typedef struct
+{
+ drflac_uint32 countRemaining;
+ const char* pRunningData;
+} drflac_cuesheet_track_iterator;
+
+/* Packing is important on this structure because we map this directly to the raw data within the CUESHEET metadata block. */
+#pragma pack(4)
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 index;
+ drflac_uint8 reserved[3];
+} drflac_cuesheet_track_index;
+#pragma pack()
+
+typedef struct
+{
+ drflac_uint64 offset;
+ drflac_uint8 trackNumber;
+ char ISRC[12];
+ drflac_bool8 isAudio;
+ drflac_bool8 preEmphasis;
+ drflac_uint8 indexCount;
+ const drflac_cuesheet_track_index* pIndexPoints;
+} drflac_cuesheet_track;
+
+/*
+Initializes a cuesheet track iterator. This can be used for iterating over the cuesheet tracks in a CUESHEET metadata
+block.
+*/
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData);
+
+/* Goes to the next cuesheet track in the given iterator. If DRFLAC_FALSE is returned it means there are no more comments. */
+drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* dr_flac_h */
+
+
+/************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************
+
+ IMPLEMENTATION
+
+ ************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************/
+#ifdef DR_FLAC_IMPLEMENTATION
+
+/* Disable some annoying warnings. */
+#if defined(__GNUC__)
+ #pragma GCC diagnostic push
+ #if __GNUC__ >= 7
+ #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+ #endif
+#endif
+
+#ifdef __linux__
+ #ifndef _BSD_SOURCE
+ #define _BSD_SOURCE
+ #endif
+ #ifndef __USE_BSD
+ #define __USE_BSD
+ #endif
+ #include <endian.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _MSC_VER
+ #define DRFLAC_INLINE __forceinline
+#elif defined(__GNUC__)
+ /*
+ I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
+ the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
+ case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
+ command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
+ I am using "__inline__" only when we're compiling in strict ANSI mode.
+ */
+ #if defined(__STRICT_ANSI__)
+ #define DRFLAC_INLINE __inline__ __attribute__((always_inline))
+ #else
+ #define DRFLAC_INLINE inline __attribute__((always_inline))
+ #endif
+#else
+ #define DRFLAC_INLINE
+#endif
+
+/* 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
+
+/* Intrinsics Support */
+#if !defined(DR_FLAC_NO_SIMD)
+ #if defined(DRFLAC_X64) || defined(DRFLAC_X86)
+ #if defined(_MSC_VER) && !defined(__clang__)
+ /* MSVC. */
+ #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) /* 2005 */
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) /* 2010 */
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #else
+ /* Assume GNUC-style. */
+ #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2)
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41)
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #endif
+
+ /* If at this point we still haven't determined compiler support for the intrinsics just fall back to __has_include. */
+ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
+ #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include(<emmintrin.h>)
+ #define DRFLAC_SUPPORT_SSE2
+ #endif
+ #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include(<smmintrin.h>)
+ #define DRFLAC_SUPPORT_SSE41
+ #endif
+ #endif
+
+ #if defined(DRFLAC_SUPPORT_SSE41)
+ #include <smmintrin.h>
+ #elif defined(DRFLAC_SUPPORT_SSE2)
+ #include <emmintrin.h>
+ #endif
+ #endif
+
+ #if defined(DRFLAC_ARM)
+ #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
+ #define DRFLAC_SUPPORT_NEON
+ #endif
+
+ /* Fall back to looking for the #include file. */
+ #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
+ #if !defined(DRFLAC_SUPPORT_NEON) && !defined(DRFLAC_NO_NEON) && __has_include(<arm_neon.h>)
+ #define DRFLAC_SUPPORT_NEON
+ #endif
+ #endif
+
+ #if defined(DRFLAC_SUPPORT_NEON)
+ #include <arm_neon.h>
+ #endif
+ #endif
+#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 <intrin.h>
+ 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)
+ {
+ /*
+ It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
+ specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
+ supporting different assembly dialects.
+
+ What's basically happening is that we're saving and restoring the ebx register manually.
+ */
+ #if defined(DRFLAC_X86) && defined(__PIC__)
+ __asm__ __volatile__ (
+ "xchg{l} {%%}ebx, %k1;"
+ "cpuid;"
+ "xchg{l} {%%}ebx, %k1;"
+ : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #else
+ __asm__ __volatile__ (
+ "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #endif
+ }
+ #else
+ #define DRFLAC_NO_CPUID
+ #endif
+ #endif
+#else
+ #define DRFLAC_NO_CPUID
+#endif
+
+static DRFLAC_INLINE drflac_bool32 drflac_has_sse2()
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2)
+ #if defined(DRFLAC_X64)
+ return DRFLAC_TRUE; /* 64-bit targets always support SSE2. */
+ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
+ return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE2 code we can assume support. */
+ #else
+ #if defined(DRFLAC_NO_CPUID)
+ return DRFLAC_FALSE;
+ #else
+ int info[4];
+ drflac__cpuid(info, 1);
+ return (info[3] & (1 << 26)) != 0;
+ #endif
+ #endif
+ #else
+ return DRFLAC_FALSE; /* SSE2 is only supported on x86 and x64 architectures. */
+ #endif
+#else
+ return DRFLAC_FALSE; /* No compiler support. */
+#endif
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac_has_sse41()
+{
+#if defined(DRFLAC_SUPPORT_SSE41)
+ #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41)
+ #if defined(DRFLAC_X64)
+ return DRFLAC_TRUE; /* 64-bit targets always support SSE4.1. */
+ #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE4_1__)
+ return DRFLAC_TRUE; /* If the compiler is allowed to freely generate SSE41 code we can assume support. */
+ #else
+ #if defined(DRFLAC_NO_CPUID)
+ return DRFLAC_FALSE;
+ #else
+ int info[4];
+ drflac__cpuid(info, 1);
+ return (info[2] & (1 << 19)) != 0;
+ #endif
+ #endif
+ #else
+ return DRFLAC_FALSE; /* SSE41 is only supported on x86 and x64 architectures. */
+ #endif
+#else
+ return DRFLAC_FALSE; /* No compiler support. */
+#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 defined(__has_builtin)
+ #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
+ #define DRFLAC_HAS_LZCNT_INTRINSIC
+ #endif
+ #endif
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1300
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+#elif defined(__clang__)
+ #if defined(__has_builtin)
+ #if __has_builtin(__builtin_bswap16)
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap32)
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap64)
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+ #endif
+#elif defined(__GNUC__)
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define DRFLAC_HAS_BYTESWAP32_INTRINSIC
+ #define DRFLAC_HAS_BYTESWAP64_INTRINSIC
+ #endif
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
+ #define DRFLAC_HAS_BYTESWAP16_INTRINSIC
+ #endif
+#endif
+
+
+/* Standard library stuff. */
+#ifndef DRFLAC_ASSERT
+#include <assert.h>
+#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. */
+
+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))
+
+
+/* CPU caps. */
+#if defined(__has_feature)
+ #if __has_feature(thread_sanitizer)
+ #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
+ #else
+ #define DRFLAC_NO_THREAD_SANITIZE
+ #endif
+#else
+ #define DRFLAC_NO_THREAD_SANITIZE
+#endif
+
+#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
+static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE;
+#endif
+
+#ifndef DRFLAC_NO_CPUID
+static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE;
+static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE;
+
+/*
+I've had a bug report that Clang's ThreadSanitizer presents a warning in this function. Having reviewed this, this does
+actually make sense. However, since CPU caps should never differ for a running process, I don't think the trade off of
+complicating internal API's by passing around CPU caps versus just disabling the warnings is worthwhile. I'm therefore
+just going to disable these warnings. This is disabled via the DRFLAC_NO_THREAD_SANITIZE attribute.
+*/
+DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps()
+{
+ static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE;
+
+ if (!isCPUCapsInitialized) {
+ int info[4] = {0};
+
+ /* LZCNT */
+#if defined(DRFLAC_HAS_LZCNT_INTRINSIC)
+ drflac__cpuid(info, 0x80000001);
+ drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
+#endif
+
+ /* SSE2 */
+ drflac__gIsSSE2Supported = drflac_has_sse2();
+
+ /* SSE4.1 */
+ drflac__gIsSSE41Supported = drflac_has_sse41();
+
+ /* Initialized. */
+ isCPUCapsInitialized = DRFLAC_TRUE;
+ }
+}
+#else
+static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE;
+
+static DRFLAC_INLINE drflac_bool32 drflac__has_neon()
+{
+#if defined(DRFLAC_SUPPORT_NEON)
+ #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON)
+ #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
+ return DRFLAC_TRUE; /* If the compiler is allowed to freely generate NEON code we can assume support. */
+ #else
+ /* TODO: Runtime check. */
+ return DRFLAC_FALSE;
+ #endif
+ #else
+ return DRFLAC_FALSE; /* NEON is only supported on ARM architectures. */
+ #endif
+#else
+ return DRFLAC_FALSE; /* No compiler support. */
+#endif
+}
+
+DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps()
+{
+ drflac__gIsNEONSupported = drflac__has_neon();
+
+#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
+ drflac__gIsLZCNTSupported = DRFLAC_TRUE;
+#endif
+}
+#endif
+
+
+/* Endian Management */
+static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian()
+{
+#if defined(DRFLAC_X86) || defined(DRFLAC_X64)
+ return DRFLAC_TRUE;
+#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
+ 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_BYTESWAP16_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_BYTESWAP32_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_ulong(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */
+ /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
+ drflac_uint32 r;
+ __asm__ __volatile__ (
+ #if defined(DRFLAC_64BIT)
+ "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */
+ #else
+ "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
+ #endif
+ );
+ return r;
+ #else
+ return __builtin_bswap32(n);
+ #endif
+ #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_BYTESWAP64_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)
+{
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint16(n);
+ }
+
+ return n;
+}
+
+static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n)
+{
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+}
+
+static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n)
+{
+ if (drflac__is_little_endian()) {
+ return drflac__swap_endian_uint64(n);
+ }
+
+ return n;
+}
+
+
+static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n)
+{
+ if (!drflac__is_little_endian()) {
+ return drflac__swap_endian_uint32(n);
+ }
+
+ return n;
+}
+
+
+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)
+{
+#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;
+ drflac_uint32 leftoverBits;
+ drflac_uint64 leftoverDataMask;
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+
+ DRFLAC_ASSERT(count <= 32);
+
+ wholeBytes = count >> 3;
+ leftoverBits = count - (wholeBytes*8);
+ 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_cache(drflac_uint16 crc, drflac_cache_t data)
+{
+#ifdef DRFLAC_64BIT
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF));
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF));
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF));
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF));
+#endif
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF));
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF));
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF));
+ crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF));
+
+ return crc;
+}
+
+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;
+}
+
+#if 0
+static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count)
+{
+#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;
+ drflac_uint32 leftoverBits;
+ drflac_uint64 leftoverDataMask;
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+
+ DRFLAC_ASSERT(count <= 64);
+
+ wholeBytes = count >> 3;
+ leftoverBits = count & 7;
+ 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)
+{
+#ifdef DR_FLAC_NO_CRC
+ (void)crc;
+ (void)data;
+ (void)count;
+ return 0;
+#else
+ drflac_uint32 wholeBytes;
+ drflac_uint32 leftoverBits;
+ drflac_uint64 leftoverDataMask;
+
+ static drflac_uint64 leftoverDataMaskTable[8] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
+ };
+
+ DRFLAC_ASSERT(count <= 64);
+
+ wholeBytes = count >> 3;
+ leftoverBits = count & 7;
+ leftoverDataMask = leftoverDataMaskTable[leftoverBits];
+
+ switch (wholeBytes) {
+ default:
+ case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); /* Weird "<< 32" bitshift is required for C89 because it doesn't support 64-bit constants. Should be optimized out by a good compiler. */
+ case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
+ case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
+ case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
+ case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
+ case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
+ case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
+ case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << 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
+}
+#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)
+#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount)))
+#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_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)))
+#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)
+{
+ if (bs->crc16CacheIgnoredBytes == 0) {
+ bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache);
+ } else {
+ 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)
+{
+ size_t bytesRead;
+ size_t alignedL1LineCount;
+
+ /* 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. */
+ }
+
+ 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.
+ */
+ 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;
+ size_t i;
+ for (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)
+{
+ size_t bytesRead;
+
+#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.
+ */
+ bytesRead = bs->unalignedByteCount;
+ if (bytesRead == 0) {
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); /* <-- The stream has been exhausted, so marked the bits as consumed. */
+ 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_BITS_REMAINING(bs)); /* <-- 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 we want to load all 32-bits from a 32-bit cache we need to do it slightly differently because we can't do
+ a 32-bit shift on a 32-bit integer. This will never be the case on 64-bit caches, so we can have a slightly
+ more optimal solution for this.
+ */
+#ifdef DRFLAC_64BIT
+ *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
+ bs->consumedBits += bitCount;
+ bs->cache <<= bitCount;
+#else
+ 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 {
+ /* Cannot shift by 32-bits, so need to do it differently. */
+ *pResultOut = (drflac_uint32)bs->cache;
+ bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ bs->cache = 0;
+ }
+#endif
+
+ 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_uint32 result;
+ drflac_uint32 signbit;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(pResult != NULL);
+ DRFLAC_ASSERT(bitCount > 0);
+ DRFLAC_ASSERT(bitCount <= 32);
+
+ if (!drflac__read_uint32(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ 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_uint32 resultHi;
+ drflac_uint32 resultLo;
+
+ DRFLAC_ASSERT(bitCount <= 64);
+ DRFLAC_ASSERT(bitCount > 32);
+
+ if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) {
+ return DRFLAC_FALSE;
+ }
+
+ 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_uint64 result;
+ drflac_uint64 signbit;
+
+ DRFLAC_ASSERT(bitCount <= 64);
+
+ if (!drflac__read_uint64(bs, bitCount, &result)) {
+ return DRFLAC_FALSE;
+ }
+
+ 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_uint32 result;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(pResult != NULL);
+ DRFLAC_ASSERT(bitCount > 0);
+ DRFLAC_ASSERT(bitCount <= 16);
+
+ 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_int32 result;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(pResult != NULL);
+ DRFLAC_ASSERT(bitCount > 0);
+ DRFLAC_ASSERT(bitCount <= 16);
+
+ 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_uint32 result;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(pResult != NULL);
+ DRFLAC_ASSERT(bitCount > 0);
+ DRFLAC_ASSERT(bitCount <= 8);
+
+ 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_int32 result;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(pResult != NULL);
+ DRFLAC_ASSERT(bitCount > 0);
+ DRFLAC_ASSERT(bitCount <= 8);
+
+ 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 (;;) {
+ drflac_uint8 hi;
+
+#ifndef DR_FLAC_NO_CRC
+ drflac__reset_crc16(bs);
+#endif
+
+ 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(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)
+{
+ drflac_uint32 n;
+ static drflac_uint32 clz_table_4[] = {
+ 0,
+ 4,
+ 3, 3,
+ 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1
+ };
+
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+
+ n = clz_table_4[x >> (sizeof(x)*8 - 4)];
+ if (n == 0) {
+#ifdef DRFLAC_64BIT
+ if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
+ if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
+ if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
+ if ((x & ((drflac_uint64)0xF0000000 << 32)) == 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()
+{
+ /* Fast compile time check for ARM. */
+#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
+ return DRFLAC_TRUE;
+#else
+ /* 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
+#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__)
+ #if defined(DRFLAC_X64)
+ {
+ drflac_uint64 r;
+ __asm__ __volatile__ (
+ "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x)
+ );
+
+ return (drflac_uint32)r;
+ }
+ #elif defined(DRFLAC_X86)
+ {
+ drflac_uint32 r;
+ __asm__ __volatile__ (
+ "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x)
+ );
+
+ return r;
+ }
+ #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT) /* <-- I haven't tested 64-bit inline assembly, so only enabling this for the 32-bit build for now. */
+ {
+ unsigned int r;
+ __asm__ __volatile__ (
+ #if defined(DRFLAC_64BIT)
+ "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */
+ #else
+ "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
+ #endif
+ );
+
+ return r;
+ }
+ #else
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+ #ifdef DRFLAC_64BIT
+ return (drflac_uint32)__builtin_clzll((drflac_uint64)x);
+ #else
+ return (drflac_uint32)__builtin_clzl((drflac_uint32)x);
+ #endif
+ #endif
+ #else
+ /* Unsupported compiler. */
+ #error "This compiler does not support the lzcnt intrinsic."
+ #endif
+#endif
+}
+#endif
+
+#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC
+#include <intrin.h> /* For BitScanReverse(). */
+
+static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x)
+{
+ drflac_uint32 n;
+
+ if (x == 0) {
+ return sizeof(x)*8;
+ }
+
+#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)
+{
+#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 DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut)
+{
+ drflac_uint32 zeroCounter = 0;
+ drflac_uint32 setBitOffsetPlus1;
+
+ while (bs->cache == 0) {
+ zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ 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_uint8 crc;
+ drflac_uint64 result;
+ unsigned char utf8[7] = {0};
+ int byteCount;
+ int i;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(pNumberOut != NULL);
+ DRFLAC_ASSERT(pCRCOut != NULL);
+
+ crc = *pCRCOut;
+
+ 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;
+ }
+
+ /*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);
+
+ result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
+ for (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_int32 prediction = 0;
+
+ DRFLAC_ASSERT(order <= 32);
+
+ /* 32-bit version. */
+
+ /* VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. */
+ 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_int64 prediction;
+
+ 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
+ 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
+ {
+ int j;
+
+ prediction = 0;
+ for (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
+ 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_uint32 i;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(count > 0);
+ DRFLAC_ASSERT(pSamplesOut != NULL);
+
+ for (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+shift >= 32) {
+ 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;
+ drflac_uint32 decodedRice;
+
+ for (;;) {
+ drflac_uint8 bit;
+ if (!drflac__read_uint8(bs, 1, &bit)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (bit == 0) {
+ zeroCounter += 1;
+ } else {
+ break;
+ }
+ }
+
+ if (riceParam > 0) {
+ if (!drflac__read_uint32(bs, riceParam, &decodedRice)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ decodedRice = 0;
+ }
+
+ *pZeroCounterOut = zeroCounter;
+ *pRiceParamPartOut = decodedRice;
+ return DRFLAC_TRUE;
+}
+#endif
+
+#if 0
+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_uint32 zeroCounter;
+ drflac_uint32 setBitOffsetPlus1;
+ drflac_uint32 riceParamPart;
+ drflac_uint32 riceLength;
+
+ DRFLAC_ASSERT(riceParam > 0); /* <-- riceParam should never be 0. drflac__read_rice_parts__param_equals_zero() should be used instead for this case. */
+
+ riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam);
+
+ zeroCounter = 0;
+ while (bs->cache == 0) {
+ zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs);
+ if (!drflac__reload_cache(bs)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ setBitOffsetPlus1 = drflac__clz(bs->cache);
+ zeroCounter += setBitOffsetPlus1;
+ setBitOffsetPlus1 += 1;
+
+ riceLength = setBitOffsetPlus1 + riceParam;
+ if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) {
+ riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
+
+ bs->consumedBits += riceLength;
+ bs->cache <<= riceLength;
+ } else {
+ drflac_uint32 bitCountLo;
+ drflac_cache_t resultHi;
+
+ bs->consumedBits += riceLength;
+ bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); /* <-- Equivalent to "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. */
+ bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs);
+ resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); /* <-- Use DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE() if ever this function allows riceParam=0. */
+
+ 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 | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
+
+ bs->consumedBits += bitCountLo;
+ bs->cache <<= bitCountLo;
+ }
+
+ pZeroCounterOut[0] = zeroCounter;
+ pRiceParamPartOut[0] = riceParamPart;
+
+ return DRFLAC_TRUE;
+}
+#endif
+
+static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ /*drflac_cache_t riceParamPlus1Mask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParamPlus1);*/
+ drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ /*
+ The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ no idea how this will work in practice...
+ */
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ pZeroCounterOut[0] = lzcount;
+
+ /*
+ It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ outside of this function at a higher level.
+ */
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ /* Getting here means the rice parameter part is wholly contained within the current cache line. */
+ pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ drflac_uint32 riceParamPartHi;
+ drflac_uint32 riceParamPartLo;
+ drflac_uint32 riceParamPartLoBitCount;
+
+ /*
+ Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ line, reload the cache, and then combine it with the head of the next cache line.
+ */
+
+ /* Grab the high part of the rice parameter part. */
+ riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift);
+
+ /* Before reloading the cache we need to grab the size in bits of the low part. */
+ riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+ DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
+
+ /* Now reload the cache. */
+ 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 = riceParamPartLoBitCount;
+ #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;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
+
+ /* We should now have enough information to construct the rice parameter part. */
+ riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
+ pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ /*
+ Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ to drflac__clz() and we need to reload the cache.
+ */
+ drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
+ for (;;) {
+ 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;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+
+ lzcount = drflac__clz(bs_cache);
+ zeroCounter += lzcount;
+
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ pZeroCounterOut[0] = zeroCounter;
+ goto extract_rice_param_part;
+ }
+
+ /* Make sure the cache is restored at the end of it all. */
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam)
+{
+ drflac_uint32 riceParamPlus1 = riceParam + 1;
+ drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
+
+ /*
+ The idea here is to use local variables for the cache in an attempt to encourage the compiler to store them in registers. I have
+ no idea how this will work in practice...
+ */
+ drflac_cache_t bs_cache = bs->cache;
+ drflac_uint32 bs_consumedBits = bs->consumedBits;
+
+ /* The first thing to do is find the first unset bit. Most likely a bit will be set in the current cache line. */
+ drflac_uint32 lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ /*
+ It is most likely that the riceParam part (which comes after the zero counter) is also on this cache line. When extracting
+ this, we include the set bit from the unary coded part because it simplifies cache management. This bit will be handled
+ outside of this function at a higher level.
+ */
+ extract_rice_param_part:
+ bs_cache <<= lzcount;
+ bs_consumedBits += lzcount;
+
+ if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
+ /* Getting here means the rice parameter part is wholly contained within the current cache line. */
+ bs_cache <<= riceParamPlus1;
+ bs_consumedBits += riceParamPlus1;
+ } else {
+ /*
+ Getting here means the rice parameter part straddles the cache line. We need to read from the tail of the current cache
+ line, reload the cache, and then combine it with the head of the next cache line.
+ */
+
+ /* Before reloading the cache we need to grab the size in bits of the low part. */
+ drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
+ DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
+
+ /* Now reload the cache. */
+ 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 = riceParamPartLoBitCount;
+ #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;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
+ }
+
+ bs_cache <<= riceParamPartLoBitCount;
+ }
+ } else {
+ /*
+ Getting here means there are no bits set on the cache line. This is a less optimal case because we just wasted a call
+ to drflac__clz() and we need to reload the cache.
+ */
+ for (;;) {
+ 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;
+ }
+
+ bs_cache = bs->cache;
+ bs_consumedBits = bs->consumedBits;
+ }
+
+ lzcount = drflac__clz(bs_cache);
+ if (lzcount < sizeof(bs_cache)*8) {
+ break;
+ }
+ }
+
+ goto extract_rice_param_part;
+ }
+
+ /* Make sure the cache is restored at the end of it all. */
+ bs->cache = bs_cache;
+ bs->consumedBits = bs_consumedBits;
+
+ return DRFLAC_TRUE;
+}
+
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(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_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+ drflac_uint32 zeroCountPart0;
+ drflac_uint32 riceParamPart0;
+ drflac_uint32 riceParamMask;
+ drflac_uint32 i;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(count > 0);
+ DRFLAC_ASSERT(pSamplesOut != NULL);
+
+ (void)bitsPerSample;
+ (void)order;
+ (void)shift;
+ (void)coefficients;
+
+ riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
+
+ i = 0;
+ while (i < count) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Rice reconstruction. */
+ riceParamPart0 &= riceParamMask;
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+
+ pSamplesOut[i] = riceParamPart0;
+
+ i += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(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_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 riceParamMask;
+ const drflac_int32* pSamplesOutEnd;
+ drflac_uint32 i;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(count > 0);
+ DRFLAC_ASSERT(pSamplesOut != NULL);
+
+ if (order == 0) {
+ return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ }
+
+ riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
+ pSamplesOutEnd = pSamplesOut + (count & ~3);
+
+ if (bitsPerSample+shift > 32) {
+ while (pSamplesOut < pSamplesOutEnd) {
+ /*
+ Rice extraction. It's faster to do this one at a time against local variables than it is to use the x4 version
+ against an array. Not sure why, but perhaps it's making more efficient use of registers?
+ */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 &= riceParamMask;
+ riceParamPart1 &= riceParamMask;
+ riceParamPart2 &= riceParamMask;
+ riceParamPart3 &= riceParamMask;
+
+ 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];
+
+ 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);
+
+ pSamplesOut += 4;
+ }
+ } else {
+ while (pSamplesOut < pSamplesOutEnd) {
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
+ return DRFLAC_FALSE;
+ }
+
+ riceParamPart0 &= riceParamMask;
+ riceParamPart1 &= riceParamMask;
+ riceParamPart2 &= riceParamMask;
+ riceParamPart3 &= riceParamMask;
+
+ 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];
+
+ 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);
+
+ pSamplesOut += 4;
+ }
+ }
+
+ i = (count & ~3);
+ while (i < count) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Rice reconstruction. */
+ riceParamPart0 &= riceParamMask;
+ riceParamPart0 |= (zeroCountPart0 << riceParam);
+ riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
+ /*riceParamPart0 = (riceParamPart0 >> 1) ^ (~(riceParamPart0 & 0x01) + 1);*/
+
+ /* Sample reconstruction. */
+ if (bitsPerSample+shift > 32) {
+ 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;
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
+{
+ __m128i r;
+
+ /* Pack. */
+ r = _mm_packs_epi32(a, b);
+
+ /* a3a2 a1a0 b3b2 b1b0 -> a3a2 b3b2 a1a0 b1b0 */
+ r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
+
+ /* a3a2 b3b2 a1a0 b1b0 -> a3b3 a2b2 a1b1 a0b0 */
+ r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
+ r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
+
+ return r;
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_SSE41)
+static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a)
+{
+ return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x)
+{
+ __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
+ __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
+ return _mm_add_epi32(x64, x32);
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x)
+{
+ return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
+}
+
+static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count)
+{
+ /*
+ To simplify this we are assuming count < 32. This restriction allows us to work on a low side and a high side. The low side
+ is shifted with zero bits, whereas the right side is shifted with sign bits.
+ */
+ __m128i lo = _mm_srli_epi64(x, count);
+ __m128i hi = _mm_srai_epi32(x, count);
+
+ hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); /* The high part needs to have the low part cleared. */
+
+ return _mm_or_si128(lo, hi);
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ int i;
+ drflac_uint32 riceParamMask;
+ drflac_int32* pDecodedSamples = pSamplesOut;
+ drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
+ drflac_uint32 zeroCountParts0;
+ drflac_uint32 zeroCountParts1;
+ drflac_uint32 zeroCountParts2;
+ drflac_uint32 zeroCountParts3;
+ drflac_uint32 riceParamParts0;
+ drflac_uint32 riceParamParts1;
+ drflac_uint32 riceParamParts2;
+ drflac_uint32 riceParamParts3;
+ __m128i coefficients128_0;
+ __m128i coefficients128_4;
+ __m128i coefficients128_8;
+ __m128i samples128_0;
+ __m128i samples128_4;
+ __m128i samples128_8;
+ __m128i riceParamMask128;
+
+ const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
+ riceParamMask128 = _mm_set1_epi32(riceParamMask);
+
+ /* Pre-load. */
+ coefficients128_0 = _mm_setzero_si128();
+ coefficients128_4 = _mm_setzero_si128();
+ coefficients128_8 = _mm_setzero_si128();
+
+ samples128_0 = _mm_setzero_si128();
+ samples128_4 = _mm_setzero_si128();
+ samples128_8 = _mm_setzero_si128();
+
+ /*
+ Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than
+ what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results
+ in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted
+ so I think there's opportunity for this to be simplified.
+ */
+#if 1
+ {
+ int runningOrder = order;
+
+ /* 0 - 3. */
+ if (runningOrder >= 4) {
+ coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
+ samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break;
+ case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break;
+ case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
+ }
+ runningOrder = 0;
+ }
+
+ /* 4 - 7 */
+ if (runningOrder >= 4) {
+ coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
+ samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break;
+ case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break;
+ case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
+ }
+ runningOrder = 0;
+ }
+
+ /* 8 - 11 */
+ if (runningOrder == 4) {
+ coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
+ samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break;
+ case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break;
+ case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
+ }
+ runningOrder = 0;
+ }
+
+ /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */
+ coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
+ coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
+ coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
+ }
+#else
+ /* This causes strict-aliasing warnings with GCC. */
+ switch (order)
+ {
+ case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
+ case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
+ case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
+ case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
+ case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
+ case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
+ case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
+ case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
+ case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
+ case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
+ case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
+ case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
+ }
+#endif
+
+ /* For this version we are doing one sample at a time. */
+ while (pDecodedSamples < pDecodedSamplesEnd) {
+ __m128i prediction128;
+ __m128i zeroCountPart128;
+ __m128i riceParamPart128;
+
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
+ return DRFLAC_FALSE;
+ }
+
+ zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
+ riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
+
+ riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
+ riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
+ riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); /* <-- SSE2 compatible */
+ /*riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_mullo_epi32(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01)), _mm_set1_epi32(0xFFFFFFFF)));*/ /* <-- Only supported from SSE4.1 and is slower in my testing... */
+
+ if (order <= 4) {
+ for (i = 0; i < 4; i += 1) {
+ prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
+
+ /* Horizontal add and shift. */
+ prediction128 = drflac__mm_hadd_epi32(prediction128);
+ prediction128 = _mm_srai_epi32(prediction128, shift);
+ prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
+
+ samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
+ riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
+ }
+ } else if (order <= 8) {
+ for (i = 0; i < 4; i += 1) {
+ prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
+ prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
+
+ /* Horizontal add and shift. */
+ prediction128 = drflac__mm_hadd_epi32(prediction128);
+ prediction128 = _mm_srai_epi32(prediction128, shift);
+ prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
+
+ samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
+ samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
+ riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
+ }
+ } else {
+ for (i = 0; i < 4; i += 1) {
+ prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
+ prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));
+ prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
+
+ /* Horizontal add and shift. */
+ prediction128 = drflac__mm_hadd_epi32(prediction128);
+ prediction128 = _mm_srai_epi32(prediction128, shift);
+ prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
+
+ samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
+ samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
+ samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
+ riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
+ }
+ }
+
+ /* We store samples in groups of 4. */
+ _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
+ pDecodedSamples += 4;
+ }
+
+ /* Make sure we process the last few samples. */
+ i = (count & ~3);
+ while (i < (int)count) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Rice reconstruction. */
+ riceParamParts0 &= riceParamMask;
+ riceParamParts0 |= (zeroCountParts0 << riceParam);
+ riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
+
+ /* Sample reconstruction. */
+ pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
+
+ i += 1;
+ pDecodedSamples += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ int i;
+ drflac_uint32 riceParamMask;
+ drflac_int32* pDecodedSamples = pSamplesOut;
+ drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
+ drflac_uint32 zeroCountParts0;
+ drflac_uint32 zeroCountParts1;
+ drflac_uint32 zeroCountParts2;
+ drflac_uint32 zeroCountParts3;
+ drflac_uint32 riceParamParts0;
+ drflac_uint32 riceParamParts1;
+ drflac_uint32 riceParamParts2;
+ drflac_uint32 riceParamParts3;
+ __m128i coefficients128_0;
+ __m128i coefficients128_4;
+ __m128i coefficients128_8;
+ __m128i samples128_0;
+ __m128i samples128_4;
+ __m128i samples128_8;
+ __m128i prediction128;
+ __m128i riceParamMask128;
+
+ const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ DRFLAC_ASSERT(order <= 12);
+
+ riceParamMask = (drflac_uint32)~((~0UL) << riceParam);
+ riceParamMask128 = _mm_set1_epi32(riceParamMask);
+
+ prediction128 = _mm_setzero_si128();
+
+ /* Pre-load. */
+ coefficients128_0 = _mm_setzero_si128();
+ coefficients128_4 = _mm_setzero_si128();
+ coefficients128_8 = _mm_setzero_si128();
+
+ samples128_0 = _mm_setzero_si128();
+ samples128_4 = _mm_setzero_si128();
+ samples128_8 = _mm_setzero_si128();
+
+#if 1
+ {
+ int runningOrder = order;
+
+ /* 0 - 3. */
+ if (runningOrder >= 4) {
+ coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
+ samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break;
+ case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break;
+ case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
+ }
+ runningOrder = 0;
+ }
+
+ /* 4 - 7 */
+ if (runningOrder >= 4) {
+ coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
+ samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break;
+ case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break;
+ case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
+ }
+ runningOrder = 0;
+ }
+
+ /* 8 - 11 */
+ if (runningOrder == 4) {
+ coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
+ samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break;
+ case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break;
+ case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
+ }
+ runningOrder = 0;
+ }
+
+ /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */
+ coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
+ coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
+ coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
+ }
+#else
+ switch (order)
+ {
+ case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12];
+ case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11];
+ case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10];
+ case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
+ case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
+ case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
+ case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
+ case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
+ case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
+ case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
+ case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
+ case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
+ }
+#endif
+
+ /* For this version we are doing one sample at a time. */
+ while (pDecodedSamples < pDecodedSamplesEnd) {
+ __m128i zeroCountPart128;
+ __m128i riceParamPart128;
+
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
+ return DRFLAC_FALSE;
+ }
+
+ zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
+ riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
+
+ riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
+ riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
+ riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1)));
+
+ for (i = 0; i < 4; i += 1) {
+ prediction128 = _mm_xor_si128(prediction128, prediction128); /* Reset to 0. */
+
+ switch (order)
+ {
+ case 12:
+ case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0))));
+ case 10:
+ case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2))));
+ case 8:
+ case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0))));
+ case 6:
+ case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2))));
+ case 4:
+ case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0))));
+ case 2:
+ case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2))));
+ }
+
+ /* Horizontal add and shift. */
+ prediction128 = drflac__mm_hadd_epi64(prediction128);
+ prediction128 = drflac__mm_srai_epi64(prediction128, shift);
+ prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
+
+ /* Our value should be sitting in prediction128[0]. We need to combine this with our SSE samples. */
+ samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
+ samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
+ samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
+
+ /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */
+ riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
+ }
+
+ /* We store samples in groups of 4. */
+ _mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
+ pDecodedSamples += 4;
+ }
+
+ /* Make sure we process the last few samples. */
+ i = (count & ~3);
+ while (i < (int)count) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Rice reconstruction. */
+ riceParamParts0 &= riceParamMask;
+ riceParamParts0 |= (zeroCountParts0 << riceParam);
+ riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
+
+ /* Sample reconstruction. */
+ pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
+
+ i += 1;
+ pDecodedSamples += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(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);
+
+ /* In my testing the order is rarely > 12, so in this case I'm going to simplify the SSE implementation by only handling order <= 12. */
+ if (order > 0 && order <= 12) {
+ if (bitsPerSample+shift > 32) {
+ return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+ } else {
+ return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+ }
+ } else {
+ return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x)
+{
+ vst1q_s32(p+0, x.val[0]);
+ vst1q_s32(p+4, x.val[1]);
+}
+
+static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x)
+{
+ vst1q_f32(p+0, x.val[0]);
+ vst1q_f32(p+4, x.val[1]);
+}
+
+static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x)
+{
+ vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
+}
+
+static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0)
+{
+ drflac_int32 x[4];
+ x[3] = x3;
+ x[2] = x2;
+ x[1] = x1;
+ x[0] = x0;
+ return vld1q_s32(x);
+}
+
+static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b)
+{
+ /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */
+
+ /* Reference */
+ /*return drflac__vdupq_n_s32x4(
+ vgetq_lane_s32(a, 0),
+ vgetq_lane_s32(b, 3),
+ vgetq_lane_s32(b, 2),
+ vgetq_lane_s32(b, 1)
+ );*/
+
+ return vextq_s32(b, a, 1);
+}
+
+static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
+{
+ /* Equivalent to SSE's _mm_alignr_epi8(a, b, 4) */
+
+ /* Reference */
+ /*return drflac__vdupq_n_s32x4(
+ vgetq_lane_s32(a, 0),
+ vgetq_lane_s32(b, 3),
+ vgetq_lane_s32(b, 2),
+ vgetq_lane_s32(b, 1)
+ );*/
+
+ return vextq_u32(b, a, 1);
+}
+
+static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x)
+{
+ /* The sum must end up in position 0. */
+
+ /* Reference */
+ /*return vdupq_n_s32(
+ vgetq_lane_s32(x, 3) +
+ vgetq_lane_s32(x, 2) +
+ vgetq_lane_s32(x, 1) +
+ vgetq_lane_s32(x, 0)
+ );*/
+
+ int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
+ return vpadd_s32(r, r);
+}
+
+static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x)
+{
+ return vadd_s64(vget_high_s64(x), vget_low_s64(x));
+}
+
+static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x)
+{
+ /* Reference */
+ /*return drflac__vdupq_n_s32x4(
+ vgetq_lane_s32(x, 0),
+ vgetq_lane_s32(x, 1),
+ vgetq_lane_s32(x, 2),
+ vgetq_lane_s32(x, 3)
+ );*/
+
+ return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
+}
+
+static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x)
+{
+ return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
+}
+
+static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x)
+{
+ return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ int i;
+ drflac_uint32 riceParamMask;
+ drflac_int32* pDecodedSamples = pSamplesOut;
+ drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
+ drflac_uint32 zeroCountParts[4];
+ drflac_uint32 riceParamParts[4];
+ int32x4_t coefficients128_0;
+ int32x4_t coefficients128_4;
+ int32x4_t coefficients128_8;
+ int32x4_t samples128_0;
+ int32x4_t samples128_4;
+ int32x4_t samples128_8;
+ uint32x4_t riceParamMask128;
+ int32x4_t riceParam128;
+ int32x2_t shift64;
+ uint32x4_t one128;
+
+ const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ riceParamMask = ~((~0UL) << riceParam);
+ riceParamMask128 = vdupq_n_u32(riceParamMask);
+
+ riceParam128 = vdupq_n_s32(riceParam);
+ shift64 = vdup_n_s32(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */
+ one128 = vdupq_n_u32(1);
+
+ /*
+ Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than
+ what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results
+ in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted
+ so I think there's opportunity for this to be simplified.
+ */
+ {
+ int runningOrder = order;
+ drflac_int32 tempC[4] = {0, 0, 0, 0};
+ drflac_int32 tempS[4] = {0, 0, 0, 0};
+
+ /* 0 - 3. */
+ if (runningOrder >= 4) {
+ coefficients128_0 = vld1q_s32(coefficients + 0);
+ samples128_0 = vld1q_s32(pSamplesOut - 4);
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */
+ case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */
+ case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */
+ }
+
+ coefficients128_0 = vld1q_s32(tempC);
+ samples128_0 = vld1q_s32(tempS);
+ runningOrder = 0;
+ }
+
+ /* 4 - 7 */
+ if (runningOrder >= 4) {
+ coefficients128_4 = vld1q_s32(coefficients + 4);
+ samples128_4 = vld1q_s32(pSamplesOut - 8);
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */
+ case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */
+ case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */
+ }
+
+ coefficients128_4 = vld1q_s32(tempC);
+ samples128_4 = vld1q_s32(tempS);
+ runningOrder = 0;
+ }
+
+ /* 8 - 11 */
+ if (runningOrder == 4) {
+ coefficients128_8 = vld1q_s32(coefficients + 8);
+ samples128_8 = vld1q_s32(pSamplesOut - 12);
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */
+ case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */
+ case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */
+ }
+
+ coefficients128_8 = vld1q_s32(tempC);
+ samples128_8 = vld1q_s32(tempS);
+ runningOrder = 0;
+ }
+
+ /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */
+ coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
+ coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
+ coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
+ }
+
+ /* For this version we are doing one sample at a time. */
+ while (pDecodedSamples < pDecodedSamplesEnd) {
+ int32x4_t prediction128;
+ int32x2_t prediction64;
+ uint32x4_t zeroCountPart128;
+ uint32x4_t riceParamPart128;
+
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
+ return DRFLAC_FALSE;
+ }
+
+ zeroCountPart128 = vld1q_u32(zeroCountParts);
+ riceParamPart128 = vld1q_u32(riceParamParts);
+
+ riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
+ riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
+ riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
+
+ if (order <= 4) {
+ for (i = 0; i < 4; i += 1) {
+ prediction128 = vmulq_s32(coefficients128_0, samples128_0);
+
+ /* Horizontal add and shift. */
+ prediction64 = drflac__vhaddq_s32(prediction128);
+ prediction64 = vshl_s32(prediction64, shift64);
+ prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
+
+ samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
+ riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
+ }
+ } else if (order <= 8) {
+ for (i = 0; i < 4; i += 1) {
+ prediction128 = vmulq_s32(coefficients128_4, samples128_4);
+ prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
+
+ /* Horizontal add and shift. */
+ prediction64 = drflac__vhaddq_s32(prediction128);
+ prediction64 = vshl_s32(prediction64, shift64);
+ prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
+
+ samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
+ samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
+ riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
+ }
+ } else {
+ for (i = 0; i < 4; i += 1) {
+ prediction128 = vmulq_s32(coefficients128_8, samples128_8);
+ prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);
+ prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
+
+ /* Horizontal add and shift. */
+ prediction64 = drflac__vhaddq_s32(prediction128);
+ prediction64 = vshl_s32(prediction64, shift64);
+ prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
+
+ samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
+ samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
+ samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
+ riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
+ }
+ }
+
+ /* We store samples in groups of 4. */
+ vst1q_s32(pDecodedSamples, samples128_0);
+ pDecodedSamples += 4;
+ }
+
+ /* Make sure we process the last few samples. */
+ i = (count & ~3);
+ while (i < (int)count) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Rice reconstruction. */
+ riceParamParts[0] &= riceParamMask;
+ riceParamParts[0] |= (zeroCountParts[0] << riceParam);
+ riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
+
+ /* Sample reconstruction. */
+ pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
+
+ i += 1;
+ pDecodedSamples += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut)
+{
+ int i;
+ drflac_uint32 riceParamMask;
+ drflac_int32* pDecodedSamples = pSamplesOut;
+ drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
+ drflac_uint32 zeroCountParts[4];
+ drflac_uint32 riceParamParts[4];
+ int32x4_t coefficients128_0;
+ int32x4_t coefficients128_4;
+ int32x4_t coefficients128_8;
+ int32x4_t samples128_0;
+ int32x4_t samples128_4;
+ int32x4_t samples128_8;
+ uint32x4_t riceParamMask128;
+ int32x4_t riceParam128;
+ int64x1_t shift64;
+ uint32x4_t one128;
+
+ const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
+
+ riceParamMask = ~((~0UL) << riceParam);
+ riceParamMask128 = vdupq_n_u32(riceParamMask);
+
+ riceParam128 = vdupq_n_s32(riceParam);
+ shift64 = vdup_n_s64(-shift); /* Negate the shift because we'll be doing a variable shift using vshlq_s32(). */
+ one128 = vdupq_n_u32(1);
+
+ /*
+ Pre-loading the coefficients and prior samples is annoying because we need to ensure we don't try reading more than
+ what's available in the input buffers. It would be conenient to use a fall-through switch to do this, but this results
+ in strict aliasing warnings with GCC. To work around this I'm just doing something hacky. This feels a bit convoluted
+ so I think there's opportunity for this to be simplified.
+ */
+ {
+ int runningOrder = order;
+ drflac_int32 tempC[4] = {0, 0, 0, 0};
+ drflac_int32 tempS[4] = {0, 0, 0, 0};
+
+ /* 0 - 3. */
+ if (runningOrder >= 4) {
+ coefficients128_0 = vld1q_s32(coefficients + 0);
+ samples128_0 = vld1q_s32(pSamplesOut - 4);
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3]; /* fallthrough */
+ case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2]; /* fallthrough */
+ case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1]; /* fallthrough */
+ }
+
+ coefficients128_0 = vld1q_s32(tempC);
+ samples128_0 = vld1q_s32(tempS);
+ runningOrder = 0;
+ }
+
+ /* 4 - 7 */
+ if (runningOrder >= 4) {
+ coefficients128_4 = vld1q_s32(coefficients + 4);
+ samples128_4 = vld1q_s32(pSamplesOut - 8);
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7]; /* fallthrough */
+ case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6]; /* fallthrough */
+ case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5]; /* fallthrough */
+ }
+
+ coefficients128_4 = vld1q_s32(tempC);
+ samples128_4 = vld1q_s32(tempS);
+ runningOrder = 0;
+ }
+
+ /* 8 - 11 */
+ if (runningOrder == 4) {
+ coefficients128_8 = vld1q_s32(coefficients + 8);
+ samples128_8 = vld1q_s32(pSamplesOut - 12);
+ runningOrder -= 4;
+ } else {
+ switch (runningOrder) {
+ case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11]; /* fallthrough */
+ case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10]; /* fallthrough */
+ case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9]; /* fallthrough */
+ }
+
+ coefficients128_8 = vld1q_s32(tempC);
+ samples128_8 = vld1q_s32(tempS);
+ runningOrder = 0;
+ }
+
+ /* Coefficients need to be shuffled for our streaming algorithm below to work. Samples are already in the correct order from the loading routine above. */
+ coefficients128_0 = drflac__vrevq_s32(coefficients128_0);
+ coefficients128_4 = drflac__vrevq_s32(coefficients128_4);
+ coefficients128_8 = drflac__vrevq_s32(coefficients128_8);
+ }
+
+ /* For this version we are doing one sample at a time. */
+ while (pDecodedSamples < pDecodedSamplesEnd) {
+ int64x2_t prediction128;
+ uint32x4_t zeroCountPart128;
+ uint32x4_t riceParamPart128;
+
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
+ !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
+ return DRFLAC_FALSE;
+ }
+
+ zeroCountPart128 = vld1q_u32(zeroCountParts);
+ riceParamPart128 = vld1q_u32(riceParamParts);
+
+ riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
+ riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
+ riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
+
+ for (i = 0; i < 4; i += 1) {
+ int64x1_t prediction64;
+
+ prediction128 = veorq_s64(prediction128, prediction128); /* Reset to 0. */
+ switch (order)
+ {
+ case 12:
+ case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
+ case 10:
+ case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
+ case 8:
+ case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
+ case 6:
+ case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
+ case 4:
+ case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
+ case 2:
+ case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
+ }
+
+ /* Horizontal add and shift. */
+ prediction64 = drflac__vhaddq_s64(prediction128);
+ prediction64 = vshl_s64(prediction64, shift64);
+ prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
+
+ /* Our value should be sitting in prediction64[0]. We need to combine this with our SSE samples. */
+ samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8);
+ samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4);
+ samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
+
+ /* Slide our rice parameter down so that the value in position 0 contains the next one to process. */
+ riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
+ }
+
+ /* We store samples in groups of 4. */
+ vst1q_s32(pDecodedSamples, samples128_0);
+ pDecodedSamples += 4;
+ }
+
+ /* Make sure we process the last few samples. */
+ i = (count & ~3);
+ while (i < (int)count) {
+ /* Rice extraction. */
+ if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Rice reconstruction. */
+ riceParamParts[0] &= riceParamMask;
+ riceParamParts[0] |= (zeroCountParts[0] << riceParam);
+ riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
+
+ /* Sample reconstruction. */
+ pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
+
+ i += 1;
+ pDecodedSamples += 1;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(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);
+
+ /* In my testing the order is rarely > 12, so in this case I'm going to simplify the NEON implementation by only handling order <= 12. */
+ if (order > 0 && order <= 12) {
+ if (bitsPerSample+shift > 32) {
+ return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+ } else {
+ return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, order, shift, coefficients, pSamplesOut);
+ }
+ } else {
+ return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ }
+}
+#endif
+
+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 defined(DRFLAC_SUPPORT_SSE41)
+ if (drflac__gIsSSE41Supported) {
+ return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported) {
+ return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, order, shift, coefficients, pSamplesOut);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+ #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__scalar(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_uint32 i;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(count > 0);
+
+ for (i = 0; i < count; ++i) {
+ if (!drflac__seek_rice_parts(bs, riceParam)) {
+ 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_uint32 i;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(count > 0);
+ DRFLAC_ASSERT(unencodedBitsPerSample <= 31); /* <-- unencodedBitsPerSample is a 5 bit number, so cannot exceed 31. */
+ DRFLAC_ASSERT(pSamplesOut != NULL);
+
+ for (i = 0; i < count; ++i) {
+ if (unencodedBitsPerSample > 0) {
+ if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ pSamplesOut[i] = 0;
+ }
+
+ if (bitsPerSample >= 24) {
+ 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 <order> residuals will be ignored. The
+<blockSize> and <order> 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_uint8 residualMethod;
+ drflac_uint8 partitionOrder;
+ drflac_uint32 samplesInPartition;
+ drflac_uint32 partitionsRemaining;
+
+ 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? */
+
+ 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 <order> values. */
+ pDecodedSamples += order;
+
+ 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;
+ }
+
+ samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ 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 == 15) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 31) {
+ 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 <order> residuals will be set to 0. The
+<blockSize> and <order> 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_uint8 residualMethod;
+ drflac_uint8 partitionOrder;
+ drflac_uint32 samplesInPartition;
+ drflac_uint32 partitionsRemaining;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(blockSize != 0);
+
+ 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. */
+ }
+
+ 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;
+ }
+
+ samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
+ 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 == 15) {
+ riceParam = 0xFF;
+ }
+ } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
+ if (!drflac__read_uint8(bs, 5, &riceParam)) {
+ return DRFLAC_FALSE;
+ }
+ if (riceParam == 31) {
+ 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 subframeBitsPerSample, drflac_int32* pDecodedSamples)
+{
+ drflac_uint32 i;
+
+ /* Only a single sample needs to be decoded here. */
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, subframeBitsPerSample, &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 (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 subframeBitsPerSample, drflac_int32* pDecodedSamples)
+{
+ drflac_uint32 i;
+
+ for (i = 0; i < blockSize; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, subframeBitsPerSample, &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 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples)
+{
+ drflac_uint32 i;
+
+ static 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 (i = 0; i < lpcOrder; ++i) {
+ drflac_int32 sample;
+ if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) {
+ return DRFLAC_FALSE;
+ }
+
+ pDecodedSamples[i] = sample;
+ }
+
+ if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, 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;
+ drflac_uint8 lpcPrecision;
+ drflac_int8 lpcShift;
+ drflac_int32 coefficients[32];
+
+ /* 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;
+ }
+
+ if (!drflac__read_uint8(bs, 4, &lpcPrecision)) {
+ return DRFLAC_FALSE;
+ }
+ if (lpcPrecision == 15) {
+ return DRFLAC_FALSE; /* Invalid. */
+ }
+ lpcPrecision += 1;
+
+ if (!drflac__read_int8(bs, 5, &lpcShift)) {
+ return DRFLAC_FALSE;
+ }
+
+ DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
+ 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_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header)
+{
+ 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. */
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(header != NULL);
+
+ /* Keep looping until we find a valid sync code. */
+ for (;;) {
+ drflac_uint8 crc8 = 0xCE; /* 0xCE = drflac_crc8(0, 0x3FFE, 14); */
+ drflac_uint8 reserved = 0;
+ drflac_uint8 blockingStrategy = 0;
+ drflac_uint8 blockSize = 0;
+ drflac_uint8 sampleRate = 0;
+ drflac_uint8 channelAssignment = 0;
+ drflac_uint8 bitsPerSample = 0;
+ drflac_bool32 isVariableBlockSize;
+
+ if (!drflac__find_and_seek_to_next_sync_code(bs)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DRFLAC_FALSE;
+ }
+ if (reserved == 1) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, reserved, 1);
+
+ if (!drflac__read_uint8(bs, 1, &blockingStrategy)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, blockingStrategy, 1);
+
+ if (!drflac__read_uint8(bs, 4, &blockSize)) {
+ return DRFLAC_FALSE;
+ }
+ if (blockSize == 0) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, blockSize, 4);
+
+ if (!drflac__read_uint8(bs, 4, &sampleRate)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, sampleRate, 4);
+
+ if (!drflac__read_uint8(bs, 4, &channelAssignment)) {
+ return DRFLAC_FALSE;
+ }
+ if (channelAssignment > 10) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, channelAssignment, 4);
+
+ if (!drflac__read_uint8(bs, 3, &bitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ if (bitsPerSample == 3 || bitsPerSample == 7) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, bitsPerSample, 3);
+
+
+ if (!drflac__read_uint8(bs, 1, &reserved)) {
+ return DRFLAC_FALSE;
+ }
+ if (reserved == 1) {
+ continue;
+ }
+ crc8 = drflac_crc8(crc8, reserved, 1);
+
+
+ isVariableBlockSize = blockingStrategy == 1;
+ if (isVariableBlockSize) {
+ drflac_uint64 pcmFrameNumber;
+ drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_END_OF_STREAM) {
+ return DRFLAC_FALSE;
+ } else {
+ continue;
+ }
+ }
+ header->flacFrameNumber = 0;
+ header->pcmFrameNumber = pcmFrameNumber;
+ } else {
+ drflac_uint64 flacFrameNumber = 0;
+ drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
+ if (result != DRFLAC_SUCCESS) {
+ if (result == DRFLAC_END_OF_STREAM) {
+ return DRFLAC_FALSE;
+ } else {
+ continue;
+ }
+ }
+ header->flacFrameNumber = (drflac_uint32)flacFrameNumber; /* <-- Safe cast. */
+ header->pcmFrameNumber = 0;
+ }
+
+
+ if (blockSize == 1) {
+ header->blockSizeInPCMFrames = 192;
+ } else if (blockSize >= 2 && blockSize <= 5) {
+ header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
+ } else if (blockSize == 6) {
+ if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8);
+ header->blockSizeInPCMFrames += 1;
+ } else if (blockSize == 7) {
+ if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
+ return DRFLAC_FALSE;
+ }
+ crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16);
+ header->blockSizeInPCMFrames += 1;
+ } else {
+ header->blockSizeInPCMFrames = 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;
+ int type;
+
+ if (!drflac__read_uint8(bs, 8, &header)) {
+ return DRFLAC_FALSE;
+ }
+
+ /* First bit should always be 0. */
+ if ((header & 0x80) != 0) {
+ return DRFLAC_FALSE;
+ }
+
+ 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_subframe* pSubframe;
+ drflac_uint32 subframeBitsPerSample;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(frame != NULL);
+
+ 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... */
+ subframeBitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ subframeBitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ subframeBitsPerSample += 1;
+ }
+
+ /* Need to handle wasted bits per sample. */
+ if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
+ return DRFLAC_FALSE;
+ }
+ subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
+
+ pSubframe->pSamplesS32 = pDecodedSamplesOut;
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
+ } break;
+
+ default: return DRFLAC_FALSE;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex)
+{
+ drflac_subframe* pSubframe;
+ drflac_uint32 subframeBitsPerSample;
+
+ DRFLAC_ASSERT(bs != NULL);
+ DRFLAC_ASSERT(frame != NULL);
+
+ 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... */
+ subframeBitsPerSample = frame->header.bitsPerSample;
+ if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
+ subframeBitsPerSample += 1;
+ } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
+ subframeBitsPerSample += 1;
+ }
+
+ /* Need to handle wasted bits per sample. */
+ if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
+ return DRFLAC_FALSE;
+ }
+ subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
+
+ pSubframe->pSamplesS32 = NULL;
+
+ switch (pSubframe->subframeType)
+ {
+ case DRFLAC_SUBFRAME_CONSTANT:
+ {
+ if (!drflac__seek_bits(bs, subframeBitsPerSample)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_VERBATIM:
+ {
+ unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_FIXED:
+ {
+ unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
+ return DRFLAC_FALSE;
+ }
+ } break;
+
+ case DRFLAC_SUBFRAME_LPC:
+ {
+ unsigned char lpcPrecision;
+
+ unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
+ if (!drflac__seek_bits(bs, bitsToSeek)) {
+ return DRFLAC_FALSE;
+ }
+
+ 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.blockSizeInPCMFrames, 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_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
+
+ DRFLAC_ASSERT(channelAssignment <= 10);
+ return lookup[channelAssignment];
+}
+
+static drflac_result drflac__decode_flac_frame(drflac* pFlac)
+{
+ int channelCount;
+ int i;
+ drflac_uint8 paddingSizeInBits;
+ drflac_uint16 desiredCRC16;
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16;
+#endif
+
+ /* This function should be called while the stream is sitting on the first byte after the frame header. */
+ DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
+
+ /* The frame block size must never be larger than the maximum block size defined by the FLAC stream. */
+ if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {
+ return DRFLAC_ERROR;
+ }
+
+ /* The number of channels in the frame must match the channel count from the STREAMINFO block. */
+ channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
+ if (channelCount != (int)pFlac->channels) {
+ return DRFLAC_ERROR;
+ }
+
+ for (i = 0; i < channelCount; ++i) {
+ if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
+ return DRFLAC_ERROR;
+ }
+ }
+
+ 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
+ actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+#endif
+ 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->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
+
+ return DRFLAC_SUCCESS;
+}
+
+static drflac_result drflac__seek_flac_frame(drflac* pFlac)
+{
+ int channelCount;
+ int i;
+ drflac_uint16 desiredCRC16;
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint16 actualCRC16;
+#endif
+
+ channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
+ for (i = 0; i < channelCount; ++i) {
+ if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, 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
+ actualCRC16 = drflac__flush_crc16(&pFlac->bs);
+#endif
+ 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_flac_frame(drflac* pFlac)
+{
+ DRFLAC_ASSERT(pFlac != NULL);
+
+ for (;;) {
+ drflac_result result;
+
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+
+ result = drflac__decode_flac_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_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame)
+{
+ drflac_uint64 firstPCMFrame;
+ drflac_uint64 lastPCMFrame;
+
+ DRFLAC_ASSERT(pFlac != NULL);
+
+ firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
+ if (firstPCMFrame == 0) {
+ firstPCMFrame = pFlac->currentFLACFrame.header.flacFrameNumber * pFlac->maxBlockSizeInPCMFrames;
+ }
+
+ lastPCMFrame = firstPCMFrame + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames);
+ if (lastPCMFrame > 0) {
+ lastPCMFrame -= 1; /* Needs to be zero based. */
+ }
+
+ if (pFirstPCMFrame) {
+ *pFirstPCMFrame = firstPCMFrame;
+ }
+ if (pLastPCMFrame) {
+ *pLastPCMFrame = lastPCMFrame;
+ }
+}
+
+static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac)
+{
+ drflac_bool32 result;
+
+ DRFLAC_ASSERT(pFlac != NULL);
+
+ result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
+
+ DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
+ pFlac->currentPCMFrame = 0;
+
+ return result;
+}
+
+static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_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_flac_frame(pFlac);
+}
+
+
+drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek)
+{
+ drflac_uint64 pcmFramesRead = 0;
+ while (pcmFramesToSeek > 0) {
+ if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; /* Couldn't read the next frame, so just break from the loop and return. */
+ }
+ } else {
+ if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
+ pcmFramesRead += pcmFramesToSeek;
+ pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; /* <-- Safe cast. Will always be < currentFrame.pcmFramesRemaining < 65536. */
+ pcmFramesToSeek = 0;
+ } else {
+ pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
+ pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
+ pFlac->currentFLACFrame.pcmFramesRemaining = 0;
+ }
+ }
+ }
+
+ pFlac->currentPCMFrame += pcmFramesRead;
+ return pcmFramesRead;
+}
+
+
+static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex)
+{
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
+ drflac_uint64 runningPCMFrameCount;
+
+ DRFLAC_ASSERT(pFlac != NULL);
+
+ /* 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. */
+ if (pcmFrameIndex >= pFlac->currentPCMFrame) {
+ /* Seeking forward. Need to seek from the current position. */
+ runningPCMFrameCount = pFlac->currentPCMFrame;
+
+ /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */
+ if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ isMidFrame = DRFLAC_TRUE;
+ }
+ } else {
+ /* Seeking backwards. Need to seek from the start of the file. */
+ runningPCMFrameCount = 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_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.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 pcmFrameCountInThisFLACFrame;
+ drflac_uint64 firstPCMFrameInFLACFrame = 0;
+ drflac_uint64 lastPCMFrameInFLACFrame = 0;
+
+ drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
+
+ pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
+ if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
+ /*
+ 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 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
+
+ if (!isMidFrame) {
+ drflac_result result = drflac__decode_flac_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__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- 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__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
+ }
+ } 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_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
+ } 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_flac_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ */
+ runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
+ pFlac->currentFLACFrame.pcmFramesRemaining = 0;
+ isMidFrame = DRFLAC_FALSE;
+ }
+
+ /* If we are seeking to the end of the file and we've just hit it, we're done. */
+ if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
+ return DRFLAC_TRUE;
+ }
+ }
+
+ next_iteration:
+ /* Grab the next frame in preparation for the next iteration. */
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+}
+
+
+#if !defined(DR_FLAC_NO_CRC)
+/*
+We use an average compression ratio to determine our approximate start location. FLAC files are generally about 50%-70% the size of their
+uncompressed counterparts so we'll use this as a basis. I'm going to split the middle and use a factor of 0.6 to determine the starting
+location.
+*/
+#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
+
+static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset)
+{
+ DRFLAC_ASSERT(pFlac != NULL);
+ DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
+ DRFLAC_ASSERT(targetByte >= rangeLo);
+ DRFLAC_ASSERT(targetByte <= rangeHi);
+
+ *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
+
+ for (;;) {
+ /* When seeking to a byte, failure probably means we've attempted to seek beyond the end of the stream. To counter this we just halve it each attempt. */
+ if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) {
+ /* If we couldn't even seek to the first byte in the stream we have a problem. Just abandon the whole thing. */
+ if (targetByte == 0) {
+ drflac__seek_to_first_frame(pFlac); /* Try to recover. */
+ return DRFLAC_FALSE;
+ }
+
+ /* Halve the byte location and continue. */
+ targetByte = rangeLo + ((rangeHi - rangeLo)/2);
+ rangeHi = targetByte;
+ } else {
+ /* Getting here should mean that we have seeked to an appropriate byte. */
+
+ /* Clear the details of the FLAC frame so we don't misreport data. */
+ DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
+
+ /*
+ Now seek to the next FLAC frame. We need to decode the entire frame (not just the header) because it's possible for the header to incorrectly pass the
+ CRC check and return bad data. We need to decode the entire frame to be more certain. Although this seems unlikely, this has happened to me in testing
+ to it needs to stay this way for now.
+ */
+#if 1
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ /* Halve the byte location and continue. */
+ targetByte = rangeLo + ((rangeHi - rangeLo)/2);
+ rangeHi = targetByte;
+ } else {
+ break;
+ }
+#else
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ /* Halve the byte location and continue. */
+ targetByte = rangeLo + ((rangeHi - rangeLo)/2);
+ rangeHi = targetByte;
+ } else {
+ break;
+ }
+#endif
+ }
+ }
+
+ /* The current PCM frame needs to be updated based on the frame we just seeked to. */
+ drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
+
+ DRFLAC_ASSERT(targetByte <= rangeHi);
+
+ *pLastSuccessfulSeekOffset = targetByte;
+ return DRFLAC_TRUE;
+}
+
+static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset)
+{
+ /* This section of code would be used if we were only decoding the FLAC frame header when calling drflac__seek_to_approximate_flac_frame_to_byte(). */
+#if 0
+ if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) {
+ /* We failed to decode this frame which may be due to it being corrupt. We'll just use the next valid FLAC frame. */
+ if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) {
+ return DRFLAC_FALSE;
+ }
+ }
+#endif
+
+ return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
+}
+
+
+static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi)
+{
+ /* This assumes pFlac->currentPCMFrame is sitting on byteRangeLo upon entry. */
+
+ drflac_uint64 targetByte;
+ drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
+ drflac_uint64 pcmRangeHi = 0;
+ drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1;
+ drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
+ drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
+
+ targetByte = byteRangeLo + (drflac_uint64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample/8 * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
+ if (targetByte > byteRangeHi) {
+ targetByte = byteRangeHi;
+ }
+
+ for (;;) {
+ if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
+ /* We found a FLAC frame. We need to check if it contains the sample we're looking for. */
+ drflac_uint64 newPCMRangeLo;
+ drflac_uint64 newPCMRangeHi;
+ drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
+
+ /* If we selected the same frame, it means we should be pretty close. Just decode the rest. */
+ if (pcmRangeLo == newPCMRangeLo) {
+ if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
+ break; /* Failed to seek to closest frame. */
+ }
+
+ if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
+ return DRFLAC_TRUE;
+ } else {
+ break; /* Failed to seek forward. */
+ }
+ }
+
+ pcmRangeLo = newPCMRangeLo;
+ pcmRangeHi = newPCMRangeHi;
+
+ if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
+ /* The target PCM frame is in this FLAC frame. */
+ if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
+ return DRFLAC_TRUE;
+ } else {
+ break; /* Failed to seek to FLAC frame. */
+ }
+ } else {
+ const float approxCompressionRatio = (lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / (pcmRangeLo * pFlac->channels * pFlac->bitsPerSample/8.0f);
+
+ if (pcmRangeLo > pcmFrameIndex) {
+ /* We seeked too far forward. We need to move our target byte backward and try again. */
+ byteRangeHi = lastSuccessfulSeekOffset;
+ if (byteRangeLo > byteRangeHi) {
+ byteRangeLo = byteRangeHi;
+ }
+
+ /*targetByte = lastSuccessfulSeekOffset - (drflac_uint64)((pcmRangeLo-pcmFrameIndex) * pFlac->channels * pFlac->bitsPerSample/8 * approxCompressionRatio);*/
+ targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
+ if (targetByte < byteRangeLo) {
+ targetByte = byteRangeLo;
+ }
+ } else /*if (pcmRangeHi < pcmFrameIndex)*/ {
+ /* We didn't seek far enough. We need to move our target byte forward and try again. */
+
+ /* If we're close enough we can just seek forward. */
+ if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
+ if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
+ return DRFLAC_TRUE;
+ } else {
+ break; /* Failed to seek to FLAC frame. */
+ }
+ } else {
+ byteRangeLo = lastSuccessfulSeekOffset;
+ if (byteRangeHi < byteRangeLo) {
+ byteRangeHi = byteRangeLo;
+ }
+
+ /*targetByte = byteRangeLo + (drflac_uint64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample/8 * approxCompressionRatio);*/
+ targetByte = lastSuccessfulSeekOffset + (drflac_uint64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample/8 * approxCompressionRatio);
+ /*targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);*/
+
+ if (targetByte > byteRangeHi) {
+ targetByte = byteRangeHi;
+ }
+
+ if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
+ closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
+ }
+ }
+ }
+ }
+ } else {
+ /* Getting here is really bad. We just recover as best we can, but moving to the first frame in the stream, and then abort. */
+ break;
+ }
+ }
+
+ drflac__seek_to_first_frame(pFlac); /* <-- Try to recover. */
+ return DRFLAC_FALSE;
+}
+
+static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex)
+{
+ drflac_uint64 byteRangeLo;
+ drflac_uint64 byteRangeHi;
+ drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
+
+ /* Our algorithm currently assumes the PCM frame */
+ if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) {
+ return DRFLAC_FALSE;
+ }
+
+ /* If we're close enough to the start, just move to the start and seek forward. */
+ if (pcmFrameIndex < seekForwardThreshold) {
+ return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
+ }
+
+ /*
+ Our starting byte range is the byte position of the first FLAC frame and the approximate end of the file as if it were completely uncompressed. This ensures
+ the entire file is included, even though most of the time it'll exceed the end of the actual stream. This is OK as the frame searching logic will handle it.
+ */
+ byteRangeLo = pFlac->firstFLACFramePosInBytes;
+ byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample/8);
+
+ return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
+}
+#endif /* !DR_FLAC_NO_CRC */
+
+static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex)
+{
+ drflac_uint32 iClosestSeekpoint = 0;
+ drflac_bool32 isMidFrame = DRFLAC_FALSE;
+ drflac_uint64 runningPCMFrameCount;
+ drflac_uint32 iSeekpoint;
+
+
+ DRFLAC_ASSERT(pFlac != NULL);
+
+ if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
+ return DRFLAC_FALSE;
+ }
+
+ for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
+ break;
+ }
+
+ iClosestSeekpoint = iSeekpoint;
+ }
+
+#if !defined(DR_FLAC_NO_CRC)
+ /* At this point we should know the closest seek point. We can use a binary search for this. We need to know the total sample count for this. */
+ if (pFlac->totalPCMFrameCount > 0) {
+ drflac_uint64 byteRangeLo;
+ drflac_uint64 byteRangeHi;
+
+ byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample/8);
+ byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
+
+ if (iClosestSeekpoint < pFlac->seekpointCount-1) {
+ if (pFlac->pSeekpoints[iClosestSeekpoint+1].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { /* Is it a placeholder seekpoint. */
+ byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint+1].flacFrameOffset-1; /* Must be zero based. */
+ }
+ }
+
+ if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
+ if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
+
+ if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
+ return DRFLAC_TRUE;
+ }
+ }
+ }
+ }
+#endif /* !DR_FLAC_NO_CRC */
+
+ /* Getting here means we need to use a slower algorithm because the binary search method failed or cannot be used. */
+
+ /*
+ 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.
+ */
+ if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
+ /* Optimized case. Just seek forward from where we are. */
+ runningPCMFrameCount = pFlac->currentPCMFrame;
+
+ /* The frame header for the first frame may not yet have been read. We need to do that if necessary. */
+ if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ } else {
+ isMidFrame = DRFLAC_TRUE;
+ }
+ } else {
+ /* Slower case. Seek to the start of the seekpoint and then seek forward from there. */
+ runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
+
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Grab the frame the seekpoint is sitting on in preparation for the sample-exact seeking below. */
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ for (;;) {
+ drflac_uint64 pcmFrameCountInThisFLACFrame;
+ drflac_uint64 firstPCMFrameInFLACFrame = 0;
+ drflac_uint64 lastPCMFrameInFLACFrame = 0;
+
+ drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
+
+ pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
+ if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
+ /*
+ 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 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
+
+ if (!isMidFrame) {
+ drflac_result result = drflac__decode_flac_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__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- 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__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
+ }
+ } 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_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
+ } 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_flac_frame() which only works if the decoder is sitting on the byte just after the frame header.
+ */
+ runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
+ pFlac->currentFLACFrame.pcmFramesRemaining = 0;
+ isMidFrame = DRFLAC_FALSE;
+ }
+
+ /* If we are seeking to the end of the file and we've just hit it, we're done. */
+ if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
+ return DRFLAC_TRUE;
+ }
+ }
+
+ next_iteration:
+ /* Grab the next frame in preparation for the next iteration. */
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.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 totalPCMFrameCount;
+ drflac_uint16 maxBlockSizeInPCMFrames;
+ 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 & 0x80000000UL) >> 31;
+ *blockType = (blockHeader & 0x7F000000UL) >> 24;
+ *blockSize = (blockHeader & 0x00FFFFFFUL);
+}
+
+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)
+{
+ drflac_uint32 blockSizes;
+ drflac_uint64 frameSizes = 0;
+ drflac_uint64 importantProps;
+ drflac_uint8 md5[16];
+
+ /* min/max block size. */
+ if (onRead(pUserData, &blockSizes, 4) != 4) {
+ return DRFLAC_FALSE;
+ }
+
+ /* min/max frame size. */
+ if (onRead(pUserData, &frameSizes, 6) != 6) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Sample rate, channels, bits per sample and total sample count. */
+ if (onRead(pUserData, &importantProps, 8) != 8) {
+ return DRFLAC_FALSE;
+ }
+
+ /* MD5 */
+ 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->minBlockSizeInPCMFrames = (blockSizes & 0xFFFF0000) >> 16;
+ pStreamInfo->maxBlockSizeInPCMFrames = (blockSizes & 0x0000FFFF);
+ pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40);
+ pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16);
+ pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44);
+ pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
+ pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
+ pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
+ DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
+
+ return DRFLAC_TRUE;
+}
+
+
+static void* drflac__malloc_default(size_t sz, void* pUserData)
+{
+ (void)pUserData;
+ return DRFLAC_MALLOC(sz);
+}
+
+static void* drflac__realloc_default(void* p, size_t sz, void* pUserData)
+{
+ (void)pUserData;
+ return DRFLAC_REALLOC(p, sz);
+}
+
+static void drflac__free_default(void* p, void* pUserData)
+{
+ (void)pUserData;
+ DRFLAC_FREE(p);
+}
+
+
+static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks == NULL) {
+ return NULL;
+ }
+
+ if (pAllocationCallbacks->onMalloc != NULL) {
+ return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
+ }
+
+ /* Try using realloc(). */
+ if (pAllocationCallbacks->onRealloc != NULL) {
+ return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
+ }
+
+ return NULL;
+}
+
+static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks == NULL) {
+ return NULL;
+ }
+
+ if (pAllocationCallbacks->onRealloc != NULL) {
+ return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
+ }
+
+ /* Try emulating realloc() in terms of malloc()/free(). */
+ if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
+ void* p2;
+
+ p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
+ if (p2 == NULL) {
+ return NULL;
+ }
+
+ DRFLAC_COPY_MEMORY(p2, p, szOld);
+ pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
+
+ return p2;
+ }
+
+ return NULL;
+}
+
+static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ if (p == NULL || pAllocationCallbacks == NULL) {
+ return;
+ }
+
+ if (pAllocationCallbacks->onFree != NULL) {
+ pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
+ }
+}
+
+
+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, drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ /*
+ 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_metadata metadata;
+ 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;
+
+ metadata.type = blockType;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+
+ switch (blockType)
+ {
+ case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION:
+ {
+ if (blockSize < 4) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ 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_from_callbacks(pRawData, pAllocationCallbacks);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
+ {
+ seektablePos = runningFilePos;
+ seektableSize = blockSize;
+
+ if (onMeta) {
+ drflac_uint32 iSeekpoint;
+ void* pRawData;
+
+ pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ 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 (iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) {
+ drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint;
+ pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame);
+ pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset);
+ pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount);
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
+ {
+ if (blockSize < 8) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData;
+ const char* pRunningData;
+ const char* pRunningDataEnd;
+ drflac_uint32 i;
+
+ pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ pRunningData = (const char*)pRawData;
+ pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for the rest of the block */
+ if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
+ metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for 'commentCount' comments after the block, which at minimum is a drflac_uint32 per comment */
+ if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.vorbis_comment.pComments = pRunningData;
+
+ /* Check that the comments section is valid before passing it to the callback */
+ for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
+ drflac_uint32 commentLength;
+
+ if (pRunningDataEnd - pRunningData < 4) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ commentLength = drflac__le2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+ pRunningData += commentLength;
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET:
+ {
+ if (blockSize < 396) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData;
+ const char* pRunningData;
+ const char* pRunningDataEnd;
+ drflac_uint8 iTrack;
+ drflac_uint8 iIndex;
+
+ pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ pRunningData = (const char*)pRawData;
+ pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
+ metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8;
+ metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
+ metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
+ metadata.data.cuesheet.pTrackData = pRunningData;
+
+ /* Check that the cuesheet tracks are valid before passing it to the callback */
+ for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
+ drflac_uint8 indexCount;
+ drflac_uint32 indexPointSize;
+
+ if (pRunningDataEnd - pRunningData < 36) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ /* Skip to the index point count */
+ pRunningData += 35;
+ indexCount = pRunningData[0]; pRunningData += 1;
+ indexPointSize = indexCount * sizeof(drflac_cuesheet_track_index);
+ if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ /* Endian swap. */
+ for (iIndex = 0; iIndex < indexCount; ++iIndex) {
+ drflac_cuesheet_track_index* pTrack = (drflac_cuesheet_track_index*)pRunningData;
+ pRunningData += sizeof(drflac_cuesheet_track_index);
+ pTrack->offset = drflac__be2host_64(pTrack->offset);
+ }
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ }
+ } break;
+
+ case DRFLAC_METADATA_BLOCK_TYPE_PICTURE:
+ {
+ if (blockSize < 32) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onMeta) {
+ void* pRawData;
+ const char* pRunningData;
+ const char* pRunningDataEnd;
+
+ pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+
+ pRunningData = (const char*)pRawData;
+ pRunningDataEnd = (const char*)pRawData + blockSize;
+
+ metadata.data.picture.type = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.mimeLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for the rest of the block */
+ if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength;
+ metadata.data.picture.descriptionLength = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+
+ /* Need space for the rest of the block */
+ if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+ metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength;
+ metadata.data.picture.width = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.height = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.colorDepth = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.indexColorCount = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pictureDataSize = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData;
+
+ /* Need space for the picture after the block */
+ if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { /* <-- Note the order of operations to avoid overflow to a valid value */
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ onMeta(pUserDataMD, &metadata);
+
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ }
+ } 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_from_callbacks(blockSize, pAllocationCallbacks);
+ if (pRawData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ if (onRead(pUserData, pRawData, blockSize) != blockSize) {
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ return DRFLAC_FALSE;
+ }
+
+ metadata.pRawData = pRawData;
+ metadata.rawDataSize = blockSize;
+ onMeta(pUserDataMD, &metadata);
+
+ drflac__free_from_callbacks(pRawData, pAllocationCallbacks);
+ }
+ } 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)
+{
+ /* Pre Condition: The bit stream should be sitting just past the 4-byte id header. */
+
+ drflac_uint8 isLastBlock;
+ drflac_uint8 blockType;
+ drflac_uint32 blockSize;
+
+ (void)onSeek;
+
+ pInit->container = drflac_container_native;
+
+ /* The first metadata block should be the STREAMINFO block. */
+ 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_flac_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->maxBlockSizeInPCMFrames = 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->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
+ pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; /* 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;
+ metadata.data.streaminfo = 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;
+
+#ifndef DR_FLAC_NO_CRC
+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
+};
+#endif
+
+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. */
+ drflac_uint32 i;
+ for (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;
+ int i;
+
+ for (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_uint8 data[23];
+ drflac_uint32 i;
+
+ DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32);
+
+ 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;
+
+ 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)
+{
+ drflac_uint8 id[4];
+
+ *pBytesRead = 0;
+
+ 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)) {
+ drflac_result result;
+
+ *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
+
+ 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;
+ drflac_uint32 pageBodySize;
+#ifndef DR_FLAC_NO_CRC
+ drflac_uint32 actualCRC32;
+#endif
+
+ if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->currentBytePos += bytesRead;
+
+ 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
+ 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
+
+ oggbs->currentPageHeader = header;
+ 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_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut;
+ size_t bytesRead = 0;
+
+ DRFLAC_ASSERT(oggbs != NULL);
+ DRFLAC_ASSERT(pRunningBufferOut != NULL);
+
+ /* Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. */
+ 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;
+ int bytesSeeked = 0;
+
+ DRFLAC_ASSERT(oggbs != NULL);
+ DRFLAC_ASSERT(offset >= 0); /* <-- Never seek backwards. */
+
+ /* 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);
+
+ while (bytesSeeked < offset) {
+ int bytesRemainingToSeek = offset - bytesSeeked;
+ DRFLAC_ASSERT(bytesRemainingToSeek >= 0);
+
+ if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
+ bytesSeeked += bytesRemainingToSeek;
+ (void)bytesSeeked; /* <-- Silence a dead store warning emitted by Clang Static Analyzer. */
+ 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_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
+{
+ drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ drflac_uint64 originalBytePos;
+ drflac_uint64 runningGranulePosition;
+ drflac_uint64 runningFrameBytePos;
+ drflac_uint64 runningPCMFrameCount;
+
+ DRFLAC_ASSERT(oggbs != NULL);
+
+ originalBytePos = oggbs->currentBytePos; /* For recovery. Points to the OggS identifier. */
+
+ /* First seek to the first frame. */
+ if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
+ return DRFLAC_FALSE;
+ }
+ oggbs->bytesRemainingInPage = 0;
+
+ runningGranulePosition = 0;
+ 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 >= pcmFrameIndex) {
+ 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;
+ }
+
+ 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.
+ */
+ runningPCMFrameCount = 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_flac_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.
+ */
+ drflac_uint64 firstPCMFrameInFLACFrame = 0;
+ drflac_uint64 lastPCMFrameInFLACFrame = 0;
+ drflac_uint64 pcmFrameCountInThisFrame;
+
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ return DRFLAC_FALSE;
+ }
+
+ drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
+
+ pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
+
+ /* If we are seeking to the end of the file and we've just hit it, we're done. */
+ if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
+ drflac_result result = drflac__decode_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ pFlac->currentPCMFrame = pcmFrameIndex;
+ pFlac->currentFLACFrame.pcmFramesRemaining = 0;
+ return DRFLAC_TRUE;
+ } else {
+ return DRFLAC_FALSE;
+ }
+ }
+
+ if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
+ /*
+ The sample should be in this FLAC 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_flac_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 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); /* <-- Safe cast because the maximum number of samples in a frame is 65535. */
+ if (pcmFramesToDecode == 0) {
+ return DRFLAC_TRUE;
+ }
+
+ pFlac->currentPCMFrame = runningPCMFrameCount;
+
+ return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; /* <-- 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_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ runningPCMFrameCount += pcmFrameCountInThisFrame;
+ } 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)
+{
+ drflac_ogg_page_header header;
+ drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32;
+ drflac_uint32 bytesRead = 0;
+
+ /* Pre Condition: 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.
+ */
+ if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) {
+ return DRFLAC_FALSE;
+ }
+ pInit->runningFilePos += bytesRead;
+
+ for (;;) {
+ int pageBodySize;
+
+ /* 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. */
+ 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_streaminfo streaminfo;
+ 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. */
+ }
+
+ 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->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
+ pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
+ pInit->hasMetadataBlocks = !isLastBlock;
+
+ if (onMeta) {
+ drflac_metadata metadata;
+ metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO;
+ metadata.pRawData = NULL;
+ metadata.rawDataSize = 0;
+ metadata.data.streaminfo = 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;
+ pInit->oggBosHeader = header;
+ 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)
+{
+ drflac_bool32 relaxed;
+ drflac_uint8 id[4];
+
+ 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. */
+ relaxed = container != drflac_container_unknown;
+
+ /* 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];
+ drflac_uint8 flags;
+ drflac_uint32 headerSize;
+
+ if (onRead(pUserData, header, 6) != 6) {
+ return DRFLAC_FALSE; /* Ran out of data. */
+ }
+ pInit->runningFilePos += 6;
+
+ flags = header[1];
+
+ 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));
+ pFlac->bs = pInit->bs;
+ pFlac->onMeta = pInit->onMeta;
+ pFlac->pUserDataMD = pInit->pUserDataMD;
+ pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
+ pFlac->sampleRate = pInit->sampleRate;
+ pFlac->channels = (drflac_uint8)pInit->channels;
+ pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample;
+ pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
+ 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, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac_init_info init;
+ drflac_uint32 allocationSize;
+ drflac_uint32 wholeSIMDVectorCountPerChannel;
+ drflac_uint32 decodedSamplesAllocationSize;
+#ifndef DR_FLAC_NO_OGG
+ drflac_oggbs oggbs;
+#endif
+ drflac_uint64 firstFramePos;
+ drflac_uint64 seektablePos;
+ drflac_uint32 seektableSize;
+ drflac_allocation_callbacks allocationCallbacks;
+ drflac* pFlac;
+
+ /* CPU support first. */
+ drflac__init_cpu_caps();
+
+ if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) {
+ return NULL;
+ }
+
+ if (pAllocationCallbacks != NULL) {
+ allocationCallbacks = *pAllocationCallbacks;
+ if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
+ return NULL; /* Invalid allocation callbacks. */
+ }
+ } else {
+ allocationCallbacks.pUserData = NULL;
+ allocationCallbacks.onMalloc = drflac__malloc_default;
+ allocationCallbacks.onRealloc = drflac__realloc_default;
+ allocationCallbacks.onFree = drflac__free_default;
+ }
+
+
+ /*
+ 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.
+ */
+ 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.
+ */
+ if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) {
+ wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32)));
+ } else {
+ wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1;
+ }
+
+ 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. */
+ if (init.container == drflac_container_ogg) {
+ allocationSize += sizeof(drflac_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;
+ oggbs.bosPageHeader = init.oggBosHeader;
+ 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.
+ */
+ firstFramePos = 42; /* <-- We know we are at byte 42 at this point. */
+ seektablePos = 0;
+ 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, &allocationCallbacks)) {
+ return NULL;
+ }
+
+ allocationSize += seektableSize;
+ }
+
+
+ pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
+ drflac__init_from_info(pFlac, &init);
+ pFlac->allocationCallbacks = allocationCallbacks;
+ 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);
+ *pInternalOggbs = 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->firstFLACFramePosInBytes = 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. */
+ drflac_uint32 iSeekpoint;
+ for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
+ pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
+ pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
+ pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
+ }
+ } 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->firstFLACFramePosInBytes, drflac_seek_origin_start)) {
+ drflac__free_from_callbacks(pFlac, &allocationCallbacks);
+ 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->currentFLACFrame.header = init.firstFrameHeader;
+ do
+ {
+ drflac_result result = drflac__decode_flac_frame(pFlac);
+ if (result == DRFLAC_SUCCESS) {
+ break;
+ } else {
+ if (result == DRFLAC_CRC_MISMATCH) {
+ if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
+ drflac__free_from_callbacks(pFlac, &allocationCallbacks);
+ return NULL;
+ }
+ continue;
+ } else {
+ drflac__free_from_callbacks(pFlac, &allocationCallbacks);
+ return NULL;
+ }
+ }
+ } while (1);
+ }
+
+ return pFlac;
+}
+
+
+
+#ifndef DR_FLAC_NO_STDIO
+#include <stdio.h>
+
+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); /* <-- Never seek backwards. */
+
+ return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+static FILE* drflac__fopen(const char* filename)
+{
+ FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (fopen_s(&pFile, filename, "rb") != 0) {
+ return NULL;
+ }
+#else
+ pFile = fopen(filename, "rb");
+ if (pFile == NULL) {
+ return NULL;
+ }
+#endif
+
+ return pFile;
+}
+
+
+drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+ FILE* pFile;
+
+ pFile = drflac__fopen(filename);
+ if (pFile == NULL) {
+ return NULL;
+ }
+
+ pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ fclose(pFile);
+ return NULL;
+ }
+
+ return pFlac;
+}
+
+drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+ FILE* pFile;
+
+ pFile = drflac__fopen(filename);
+ if (pFile == NULL) {
+ return NULL;
+ }
+
+ pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ fclose(pFile);
+ 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;
+ size_t bytesRemaining;
+
+ DRFLAC_ASSERT(memoryStream != NULL);
+ DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
+
+ 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); /* <-- Never seek backwards. */
+
+ if (offset > (drflac_int64)memoryStream->dataSize) {
+ return DRFLAC_FALSE;
+ }
+
+ if (origin == drflac_seek_origin_current) {
+ if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos += offset;
+ } else {
+ return DRFLAC_FALSE; /* Trying to seek too far forward. */
+ }
+ } else {
+ if ((drflac_uint32)offset <= memoryStream->dataSize) {
+ memoryStream->currentReadPos = offset;
+ } else {
+ return DRFLAC_FALSE; /* Trying to seek too far forward. */
+ }
+ }
+
+ return DRFLAC_TRUE;
+}
+
+drflac* drflac_open_memory(const void* data, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac__memory_stream memoryStream;
+ drflac* pFlac;
+
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks);
+ 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, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac__memory_stream memoryStream;
+ drflac* pFlac;
+
+ memoryStream.data = (const unsigned char*)data;
+ memoryStream.dataSize = dataSize;
+ memoryStream.currentReadPos = 0;
+ pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
+ 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, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
+}
+drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks);
+}
+
+drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
+}
+drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
+}
+
+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) {
+ fclose((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_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs;
+ DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg);
+
+ if (oggbs->onRead == drflac__on_read_stdio) {
+ fclose((FILE*)oggbs->pUserData);
+ }
+ }
+#endif
+#endif
+
+ drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+ drflac_int32 right = left - side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 left0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 left1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 left2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 left3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 right0 = left0 - side0;
+ drflac_int32 right1 = left1 - side1;
+ drflac_int32 right2 = left2 - side2;
+ drflac_int32 right3 = left3 - side3;
+
+ pOutputSamples[i*8+0] = left0;
+ pOutputSamples[i*8+1] = right0;
+ pOutputSamples[i*8+2] = left1;
+ pOutputSamples[i*8+3] = right1;
+ pOutputSamples[i*8+4] = left2;
+ pOutputSamples[i*8+5] = right2;
+ pOutputSamples[i*8+6] = left3;
+ pOutputSamples[i*8+7] = right3;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int left = pInputSamples0[i] << shift0;
+ int side = pInputSamples1[i] << shift1;
+ int right = left - side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+ __m128i right = _mm_sub_epi32(left, side);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ int32x4_t shift0_4;
+ int32x4_t shift1_4;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ shift0_4 = vdupq_n_s32(shift0);
+ shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t left;
+ int32x4_t side;
+ int32x4_t right;
+
+ left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+ right = vsubq_s32(left, side);
+
+ drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 side0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 side1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 side2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 side3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 right0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 right1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 right2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 right3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 left0 = right0 + side0;
+ drflac_int32 left1 = right1 + side1;
+ drflac_int32 left2 = right2 + side2;
+ drflac_int32 left3 = right3 + side3;
+
+ pOutputSamples[i*8+0] = left0;
+ pOutputSamples[i*8+1] = right0;
+ pOutputSamples[i*8+2] = left1;
+ pOutputSamples[i*8+3] = right1;
+ pOutputSamples[i*8+4] = left2;
+ pOutputSamples[i*8+5] = right2;
+ pOutputSamples[i*8+6] = left3;
+ pOutputSamples[i*8+7] = right3;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+ __m128i left = _mm_add_epi32(right, side);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ int32x4_t shift0_4;
+ int32x4_t shift1_4;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ shift0_4 = vdupq_n_s32(shift0);
+ shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t side;
+ int32x4_t right;
+ int32x4_t left;
+
+ side = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+ left = vaddq_s32(right, side);
+
+ drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = left;
+ pOutputSamples[i*2+1] = right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = ((mid + side) >> 1) << unusedBitsPerSample;
+ pOutputSamples[i*2+1] = ((mid - side) >> 1) << unusedBitsPerSample;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift = unusedBitsPerSample;
+ if (shift > 0) {
+ shift -= 1;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 temp0L;
+ drflac_int32 temp1L;
+ drflac_int32 temp2L;
+ drflac_int32 temp3L;
+ drflac_int32 temp0R;
+ drflac_int32 temp1R;
+ drflac_int32 temp2R;
+ drflac_int32 temp3R;
+
+ drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) << shift);
+ temp1L = ((mid1 + side1) << shift);
+ temp2L = ((mid2 + side2) << shift);
+ temp3L = ((mid3 + side3) << shift);
+
+ temp0R = ((mid0 - side0) << shift);
+ temp1R = ((mid1 - side1) << shift);
+ temp2R = ((mid2 - side2) << shift);
+ temp3R = ((mid3 - side3) << shift);
+
+ pOutputSamples[i*8+0] = temp0L;
+ pOutputSamples[i*8+1] = temp0R;
+ pOutputSamples[i*8+2] = temp1L;
+ pOutputSamples[i*8+3] = temp1R;
+ pOutputSamples[i*8+4] = temp2L;
+ pOutputSamples[i*8+5] = temp2R;
+ pOutputSamples[i*8+6] = temp3L;
+ pOutputSamples[i*8+7] = temp3R;
+ }
+ } else {
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 temp0L;
+ drflac_int32 temp1L;
+ drflac_int32 temp2L;
+ drflac_int32 temp3L;
+ drflac_int32 temp0R;
+ drflac_int32 temp1R;
+ drflac_int32 temp2R;
+ drflac_int32 temp3R;
+
+ drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) >> 1);
+ temp1L = ((mid1 + side1) >> 1);
+ temp2L = ((mid2 + side2) >> 1);
+ temp3L = ((mid3 + side3) >> 1);
+
+ temp0R = ((mid0 - side0) >> 1);
+ temp1R = ((mid1 - side1) >> 1);
+ temp2R = ((mid2 - side2) >> 1);
+ temp3R = ((mid3 - side3) >> 1);
+
+ pOutputSamples[i*8+0] = temp0L;
+ pOutputSamples[i*8+1] = temp0R;
+ pOutputSamples[i*8+2] = temp1L;
+ pOutputSamples[i*8+3] = temp1R;
+ pOutputSamples[i*8+4] = temp2L;
+ pOutputSamples[i*8+5] = temp2R;
+ pOutputSamples[i*8+6] = temp3L;
+ pOutputSamples[i*8+7] = temp3R;
+ }
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = ((mid + side) >> 1) << unusedBitsPerSample;
+ pOutputSamples[i*2+1] = ((mid - side) >> 1) << unusedBitsPerSample;
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4;
+ int shift;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift = unusedBitsPerSample;
+ if (shift == 0) {
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i mid;
+ __m128i side;
+ __m128i left;
+ __m128i right;
+
+ mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
+ right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = ((mid + side) >> 1);
+ pOutputSamples[i*2+1] = ((mid - side) >> 1);
+ }
+ } else {
+ shift -= 1;
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i mid;
+ __m128i side;
+ __m128i left;
+ __m128i right;
+
+ mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
+ right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = ((mid + side) << shift);
+ pOutputSamples[i*2+1] = ((mid - side) << shift);
+ }
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4;
+ int shift;
+ int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */
+ int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */
+ int32x4_t one4;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+ one4 = vdupq_n_s32(1);
+
+ shift = unusedBitsPerSample;
+ if (shift == 0) {
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t mid;
+ int32x4_t side;
+ int32x4_t left;
+ int32x4_t right;
+
+ mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4);
+
+ mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, one4));
+
+ left = vshrq_n_s32(vaddq_s32(mid, side), 1);
+ right = vshrq_n_s32(vsubq_s32(mid, side), 1);
+
+ drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = ((mid + side) >> 1);
+ pOutputSamples[i*2+1] = ((mid - side) >> 1);
+ }
+ } else {
+ int32x4_t shift4;
+
+ shift -= 1;
+ shift4 = vdupq_n_s32(shift);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t mid;
+ int32x4_t side;
+ int32x4_t left;
+ int32x4_t right;
+
+ mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4);
+
+ mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, one4));
+
+ left = vshlq_s32(vaddq_s32(mid, side), shift4);
+ right = vshlq_s32(vsubq_s32(mid, side), shift4);
+
+ drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = ((mid + side) << shift);
+ pOutputSamples[i*2+1] = ((mid - side) << shift);
+ }
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
+ pOutputSamples[i*2+1] = (pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 tempL0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 tempL1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 tempL2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 tempL3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 tempR0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 tempR1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 tempR2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 tempR3 = pInputSamples1[i*4+3] << shift1;
+
+ pOutputSamples[i*8+0] = tempL0;
+ pOutputSamples[i*8+1] = tempR0;
+ pOutputSamples[i*8+2] = tempL1;
+ pOutputSamples[i*8+3] = tempR1;
+ pOutputSamples[i*8+4] = tempL2;
+ pOutputSamples[i*8+5] = tempR2;
+ pOutputSamples[i*8+6] = tempL3;
+ pOutputSamples[i*8+7] = tempR3;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (pInputSamples0[i] << shift0);
+ pOutputSamples[i*2+1] = (pInputSamples1[i] << shift1);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ int shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ int shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (pInputSamples0[i] << shift0);
+ pOutputSamples[i*2+1] = (pInputSamples1[i] << shift1);
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ int shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ int shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ int32x4_t shift4_0 = vdupq_n_s32(shift0);
+ int32x4_t shift4_1 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t left;
+ int32x4_t right;
+
+ left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift4_0);
+ right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift4_1);
+
+ drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (pInputSamples0[i] << shift0);
+ pOutputSamples[i*2+1] = (pInputSamples1[i] << shift1);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut)
+{
+ drflac_uint64 framesRead;
+ drflac_int32 unusedBitsPerSample;
+
+ if (pFlac == NULL || framesToRead == 0) {
+ return 0;
+ }
+
+ if (pBufferOut == NULL) {
+ return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
+ }
+
+ unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+
+ framesRead = 0;
+ while (framesToRead > 0) {
+ /* If we've run out of samples in this frame, go to the next. */
+ if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; /* Couldn't read the next frame, so just break from the loop and return. */
+ }
+ } else {
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
+ drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
+ drflac_uint64 frameCountThisIteration = framesToRead;
+
+ if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
+ frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
+ }
+
+ if (channelCount == 2) {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
+
+ switch (pFlac->currentFLACFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+ }
+ } else {
+ /* Generic interleaving. */
+ drflac_uint64 i;
+ for (i = 0; i < frameCountThisIteration; ++i) {
+ unsigned int j;
+ for (j = 0; j < channelCount; ++j) {
+ pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
+ }
+ }
+ }
+
+ framesRead += frameCountThisIteration;
+ pBufferOut += frameCountThisIteration * channelCount;
+ framesToRead -= frameCountThisIteration;
+ pFlac->currentPCMFrame += frameCountThisIteration;
+ pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
+ }
+ }
+
+ return framesRead;
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+ drflac_int32 right = left - side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 left0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 left1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 left2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 left3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 right0 = left0 - side0;
+ drflac_int32 right1 = left1 - side1;
+ drflac_int32 right2 = left2 - side2;
+ drflac_int32 right3 = left3 - side3;
+
+ left0 >>= 16;
+ left1 >>= 16;
+ left2 >>= 16;
+ left3 >>= 16;
+
+ right0 >>= 16;
+ right1 >>= 16;
+ right2 >>= 16;
+ right3 >>= 16;
+
+ pOutputSamples[i*8+0] = (drflac_int16)left0;
+ pOutputSamples[i*8+1] = (drflac_int16)right0;
+ pOutputSamples[i*8+2] = (drflac_int16)left1;
+ pOutputSamples[i*8+3] = (drflac_int16)right1;
+ pOutputSamples[i*8+4] = (drflac_int16)left2;
+ pOutputSamples[i*8+5] = (drflac_int16)right2;
+ pOutputSamples[i*8+6] = (drflac_int16)left3;
+ pOutputSamples[i*8+7] = (drflac_int16)right3;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+ __m128i right = _mm_sub_epi32(left, side);
+
+ left = _mm_srai_epi32(left, 16);
+ right = _mm_srai_epi32(right, 16);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ int32x4_t shift0_4;
+ int32x4_t shift1_4;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ shift0_4 = vdupq_n_s32(shift0);
+ shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t left;
+ int32x4_t side;
+ int32x4_t right;
+
+ left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+ right = vsubq_s32(left, side);
+
+ left = vshrq_n_s32(left, 16);
+ right = vshrq_n_s32(right, 16);
+
+ drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+ drflac_int32 left = right + side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 side0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 side1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 side2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 side3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 right0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 right1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 right2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 right3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 left0 = right0 + side0;
+ drflac_int32 left1 = right1 + side1;
+ drflac_int32 left2 = right2 + side2;
+ drflac_int32 left3 = right3 + side3;
+
+ left0 >>= 16;
+ left1 >>= 16;
+ left2 >>= 16;
+ left3 >>= 16;
+
+ right0 >>= 16;
+ right1 >>= 16;
+ right2 >>= 16;
+ right3 >>= 16;
+
+ pOutputSamples[i*8+0] = (drflac_int16)left0;
+ pOutputSamples[i*8+1] = (drflac_int16)right0;
+ pOutputSamples[i*8+2] = (drflac_int16)left1;
+ pOutputSamples[i*8+3] = (drflac_int16)right1;
+ pOutputSamples[i*8+4] = (drflac_int16)left2;
+ pOutputSamples[i*8+5] = (drflac_int16)right2;
+ pOutputSamples[i*8+6] = (drflac_int16)left3;
+ pOutputSamples[i*8+7] = (drflac_int16)right3;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+ __m128i left = _mm_add_epi32(right, side);
+
+ left = _mm_srai_epi32(left, 16);
+ right = _mm_srai_epi32(right, 16);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ int32x4_t shift0_4;
+ int32x4_t shift1_4;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ shift0_4 = vdupq_n_s32(shift0);
+ shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t side;
+ int32x4_t right;
+ int32x4_t left;
+
+ side = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+ left = vaddq_s32(right, side);
+
+ left = vshrq_n_s32(left, 16);
+ right = vshrq_n_s32(right, 16);
+
+ drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ left >>= 16;
+ right >>= 16;
+
+ pOutputSamples[i*2+0] = (drflac_int16)left;
+ pOutputSamples[i*2+1] = (drflac_int16)right;
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (drflac_int16)((((mid + side) >> 1) << unusedBitsPerSample) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)((((mid - side) >> 1) << unusedBitsPerSample) >> 16);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ int shift = unusedBitsPerSample;
+ if (shift > 0) {
+ shift -= 1;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 temp0L;
+ drflac_int32 temp1L;
+ drflac_int32 temp2L;
+ drflac_int32 temp3L;
+ drflac_int32 temp0R;
+ drflac_int32 temp1R;
+ drflac_int32 temp2R;
+ drflac_int32 temp3R;
+
+ drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) << shift);
+ temp1L = ((mid1 + side1) << shift);
+ temp2L = ((mid2 + side2) << shift);
+ temp3L = ((mid3 + side3) << shift);
+
+ temp0R = ((mid0 - side0) << shift);
+ temp1R = ((mid1 - side1) << shift);
+ temp2R = ((mid2 - side2) << shift);
+ temp3R = ((mid3 - side3) << shift);
+
+ temp0L >>= 16;
+ temp1L >>= 16;
+ temp2L >>= 16;
+ temp3L >>= 16;
+
+ temp0R >>= 16;
+ temp1R >>= 16;
+ temp2R >>= 16;
+ temp3R >>= 16;
+
+ pOutputSamples[i*8+0] = (drflac_int16)temp0L;
+ pOutputSamples[i*8+1] = (drflac_int16)temp0R;
+ pOutputSamples[i*8+2] = (drflac_int16)temp1L;
+ pOutputSamples[i*8+3] = (drflac_int16)temp1R;
+ pOutputSamples[i*8+4] = (drflac_int16)temp2L;
+ pOutputSamples[i*8+5] = (drflac_int16)temp2R;
+ pOutputSamples[i*8+6] = (drflac_int16)temp3L;
+ pOutputSamples[i*8+7] = (drflac_int16)temp3R;
+ }
+ } else {
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 temp0L;
+ drflac_int32 temp1L;
+ drflac_int32 temp2L;
+ drflac_int32 temp3L;
+ drflac_int32 temp0R;
+ drflac_int32 temp1R;
+ drflac_int32 temp2R;
+ drflac_int32 temp3R;
+
+ drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) >> 1);
+ temp1L = ((mid1 + side1) >> 1);
+ temp2L = ((mid2 + side2) >> 1);
+ temp3L = ((mid3 + side3) >> 1);
+
+ temp0R = ((mid0 - side0) >> 1);
+ temp1R = ((mid1 - side1) >> 1);
+ temp2R = ((mid2 - side2) >> 1);
+ temp3R = ((mid3 - side3) >> 1);
+
+ temp0L >>= 16;
+ temp1L >>= 16;
+ temp2L >>= 16;
+ temp3L >>= 16;
+
+ temp0R >>= 16;
+ temp1R >>= 16;
+ temp2R >>= 16;
+ temp3R >>= 16;
+
+ pOutputSamples[i*8+0] = (drflac_int16)temp0L;
+ pOutputSamples[i*8+1] = (drflac_int16)temp0R;
+ pOutputSamples[i*8+2] = (drflac_int16)temp1L;
+ pOutputSamples[i*8+3] = (drflac_int16)temp1R;
+ pOutputSamples[i*8+4] = (drflac_int16)temp2L;
+ pOutputSamples[i*8+5] = (drflac_int16)temp2R;
+ pOutputSamples[i*8+6] = (drflac_int16)temp3L;
+ pOutputSamples[i*8+7] = (drflac_int16)temp3R;
+ }
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (drflac_int16)((((mid + side) >> 1) << unusedBitsPerSample) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)((((mid - side) >> 1) << unusedBitsPerSample) >> 16);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4;
+ drflac_int32 shift;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ shift = unusedBitsPerSample;
+ if (shift == 0) {
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i mid;
+ __m128i side;
+ __m128i left;
+ __m128i right;
+
+ mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
+ right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
+
+ left = _mm_srai_epi32(left, 16);
+ right = _mm_srai_epi32(right, 16);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) >> 1) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) >> 1) >> 16);
+ }
+ } else {
+ shift -= 1;
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i mid;
+ __m128i side;
+ __m128i left;
+ __m128i right;
+
+ mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
+ right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
+
+ left = _mm_srai_epi32(left, 16);
+ right = _mm_srai_epi32(right, 16);
+
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
+ }
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4;
+ int shift;
+ int32x4_t wbpsShift0_4; /* wbps = Wasted Bits Per Sample */
+ int32x4_t wbpsShift1_4; /* wbps = Wasted Bits Per Sample */
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ shift = unusedBitsPerSample;
+ if (shift == 0) {
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t mid;
+ int32x4_t side;
+ int32x4_t left;
+ int32x4_t right;
+
+ mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4);
+
+ mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1)));
+
+ left = vshrq_n_s32(vaddq_s32(mid, side), 1);
+ right = vshrq_n_s32(vsubq_s32(mid, side), 1);
+
+ left = vshrq_n_s32(left, 16);
+ right = vshrq_n_s32(right, 16);
+
+ drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) >> 1) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) >> 1) >> 16);
+ }
+ } else {
+ int32x4_t shift4;
+
+ shift -= 1;
+ shift4 = vdupq_n_s32(shift);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t mid;
+ int32x4_t side;
+ int32x4_t left;
+ int32x4_t right;
+
+ mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbpsShift0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbpsShift1_4);
+
+ mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1)));
+
+ left = vshlq_s32(vaddq_s32(mid, side), shift4);
+ right = vshlq_s32(vsubq_s32(mid, side), shift4);
+
+ left = vshrq_n_s32(left, 16);
+ right = vshrq_n_s32(right, 16);
+
+ drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16);
+ }
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ int shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ int shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 tempL0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 tempL1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 tempL2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 tempL3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 tempR0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 tempR1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 tempR2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 tempR3 = pInputSamples1[i*4+3] << shift1;
+
+ tempL0 >>= 16;
+ tempL1 >>= 16;
+ tempL2 >>= 16;
+ tempL3 >>= 16;
+
+ tempR0 >>= 16;
+ tempR1 >>= 16;
+ tempR2 >>= 16;
+ tempR3 >>= 16;
+
+ pOutputSamples[i*8+0] = (drflac_int16)tempL0;
+ pOutputSamples[i*8+1] = (drflac_int16)tempR0;
+ pOutputSamples[i*8+2] = (drflac_int16)tempL1;
+ pOutputSamples[i*8+3] = (drflac_int16)tempR1;
+ pOutputSamples[i*8+4] = (drflac_int16)tempL2;
+ pOutputSamples[i*8+5] = (drflac_int16)tempR2;
+ pOutputSamples[i*8+6] = (drflac_int16)tempL3;
+ pOutputSamples[i*8+7] = (drflac_int16)tempR3;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << shift0) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << shift1) >> 16);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+
+ left = _mm_srai_epi32(left, 16);
+ right = _mm_srai_epi32(right, 16);
+
+ /* At this point we have results. We can now pack and interleave these into a single __m128i object and then store the in the output buffer. */
+ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << shift0) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << shift1) >> 16);
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ int32x4_t shift0_4 = vdupq_n_s32(shift0);
+ int32x4_t shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t left;
+ int32x4_t right;
+
+ left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+
+ left = vshrq_n_s32(left, 16);
+ right = vshrq_n_s32(right, 16);
+
+ drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0[i] << shift0) >> 16);
+ pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1[i] << shift1) >> 16);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut)
+{
+ drflac_uint64 framesRead;
+ drflac_int32 unusedBitsPerSample;
+
+ if (pFlac == NULL || framesToRead == 0) {
+ return 0;
+ }
+
+ if (pBufferOut == NULL) {
+ return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
+ }
+
+ unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+
+ framesRead = 0;
+ while (framesToRead > 0) {
+ /* If we've run out of samples in this frame, go to the next. */
+ if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; /* Couldn't read the next frame, so just break from the loop and return. */
+ }
+ } else {
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
+ drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
+ drflac_uint64 frameCountThisIteration = framesToRead;
+
+ if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
+ frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
+ }
+
+ if (channelCount == 2) {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
+
+ switch (pFlac->currentFLACFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+ }
+ } else {
+ /* Generic interleaving. */
+ drflac_uint64 i;
+ for (i = 0; i < frameCountThisIteration; ++i) {
+ unsigned int j;
+ for (j = 0; j < channelCount; ++j) {
+ drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
+ pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16);
+ }
+ }
+ }
+
+ framesRead += frameCountThisIteration;
+ pBufferOut += frameCountThisIteration * channelCount;
+ framesToRead -= frameCountThisIteration;
+ pFlac->currentPCMFrame += frameCountThisIteration;
+ pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration;
+ }
+ }
+
+ return framesRead;
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 side = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+ drflac_int32 right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)(right / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 left0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 left1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 left2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 left3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 right0 = left0 - side0;
+ drflac_int32 right1 = left1 - side1;
+ drflac_int32 right2 = left2 - side2;
+ drflac_int32 right3 = left3 - side3;
+
+ pOutputSamples[i*8+0] = left0 * factor;
+ pOutputSamples[i*8+1] = right0 * factor;
+ pOutputSamples[i*8+2] = left1 * factor;
+ pOutputSamples[i*8+3] = right1 * factor;
+ pOutputSamples[i*8+4] = left2 * factor;
+ pOutputSamples[i*8+5] = right2 * factor;
+ pOutputSamples[i*8+6] = left3 * factor;
+ pOutputSamples[i*8+7] = right3 * factor;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left * factor);
+ pOutputSamples[i*2+1] = (float)(right * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ __m128 factor;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor = _mm_set1_ps(1.0f / 8388608.0f);
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+ __m128i right = _mm_sub_epi32(left, side);
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
+
+ _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
+ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ float32x4_t factor4;
+ int32x4_t shift0_4;
+ int32x4_t shift1_4;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor4 = vdupq_n_f32(1.0f / 8388608.0f);
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ shift0_4 = vdupq_n_s32(shift0);
+ shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t left;
+ int32x4_t side;
+ int32x4_t right;
+ float32x4_t leftf;
+ float32x4_t rightf;
+
+ left = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+ right = vsubq_s32(left, side);
+ leftf = vmulq_f32(vcvtq_f32_s32(left), factor4);
+ rightf = vmulq_f32(vcvtq_f32_s32(right), factor4);
+
+ drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 left = pInputSamples0[i] << shift0;
+ drflac_int32 side = pInputSamples1[i] << shift1;
+ drflac_int32 right = left - side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ for (i = 0; i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 right = pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)(right / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ drflac_int32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 side0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 side1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 side2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 side3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 right0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 right1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 right2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 right3 = pInputSamples1[i*4+3] << shift1;
+
+ drflac_int32 left0 = right0 + side0;
+ drflac_int32 left1 = right1 + side1;
+ drflac_int32 left2 = right2 + side2;
+ drflac_int32 left3 = right3 + side3;
+
+ pOutputSamples[i*8+0] = left0 * factor;
+ pOutputSamples[i*8+1] = right0 * factor;
+ pOutputSamples[i*8+2] = left1 * factor;
+ pOutputSamples[i*8+3] = right1 * factor;
+ pOutputSamples[i*8+4] = left2 * factor;
+ pOutputSamples[i*8+5] = right2 * factor;
+ pOutputSamples[i*8+6] = left3 * factor;
+ pOutputSamples[i*8+7] = right3 * factor;
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left * factor);
+ pOutputSamples[i*2+1] = (float)(right * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ __m128 factor;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor = _mm_set1_ps(1.0f / 8388608.0f);
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+ __m128i left = _mm_add_epi32(right, side);
+ __m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
+ __m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
+
+ _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
+ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 frameCount4;
+ drflac_int32 shift0;
+ drflac_int32 shift1;
+ drflac_uint64 i;
+ float32x4_t factor4;
+ int32x4_t shift0_4;
+ int32x4_t shift1_4;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor4 = vdupq_n_f32(1.0f / 8388608.0f);
+
+ shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
+ shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ shift0_4 = vdupq_n_s32(shift0);
+ shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t side;
+ int32x4_t right;
+ int32x4_t left;
+ float32x4_t leftf;
+ float32x4_t rightf;
+
+ side = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ right = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+ left = vaddq_s32(right, side);
+ leftf = vmulq_f32(vcvtq_f32_s32(left), factor4);
+ rightf = vmulq_f32(vcvtq_f32_s32(right), factor4);
+
+ drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 side = pInputSamples0[i] << shift0;
+ drflac_int32 right = pInputSamples1[i] << shift1;
+ drflac_int32 left = right + side;
+
+ pOutputSamples[i*2+0] = (float)(left / 8388608.0f);
+ pOutputSamples[i*2+1] = (float)(right / 8388608.0f);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ int shift = unusedBitsPerSample;
+ if (shift > 0) {
+ shift -= 1;
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 temp0L;
+ drflac_int32 temp1L;
+ drflac_int32 temp2L;
+ drflac_int32 temp3L;
+ drflac_int32 temp0R;
+ drflac_int32 temp1R;
+ drflac_int32 temp2R;
+ drflac_int32 temp3R;
+
+ drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) << shift);
+ temp1L = ((mid1 + side1) << shift);
+ temp2L = ((mid2 + side2) << shift);
+ temp3L = ((mid3 + side3) << shift);
+
+ temp0R = ((mid0 - side0) << shift);
+ temp1R = ((mid1 - side1) << shift);
+ temp2R = ((mid2 - side2) << shift);
+ temp3R = ((mid3 - side3) << shift);
+
+ pOutputSamples[i*8+0] = (float)(temp0L * factor);
+ pOutputSamples[i*8+1] = (float)(temp0R * factor);
+ pOutputSamples[i*8+2] = (float)(temp1L * factor);
+ pOutputSamples[i*8+3] = (float)(temp1R * factor);
+ pOutputSamples[i*8+4] = (float)(temp2L * factor);
+ pOutputSamples[i*8+5] = (float)(temp2R * factor);
+ pOutputSamples[i*8+6] = (float)(temp3L * factor);
+ pOutputSamples[i*8+7] = (float)(temp3R * factor);
+ }
+ } else {
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 temp0L;
+ drflac_int32 temp1L;
+ drflac_int32 temp2L;
+ drflac_int32 temp3L;
+ drflac_int32 temp0R;
+ drflac_int32 temp1R;
+ drflac_int32 temp2R;
+ drflac_int32 temp3R;
+
+ drflac_int32 mid0 = pInputSamples0[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid1 = pInputSamples0[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid2 = pInputSamples0[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 mid3 = pInputSamples0[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+
+ drflac_int32 side0 = pInputSamples1[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side1 = pInputSamples1[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side2 = pInputSamples1[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+ drflac_int32 side3 = pInputSamples1[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid0 = (((drflac_uint32)mid0) << 1) | (side0 & 0x01);
+ mid1 = (((drflac_uint32)mid1) << 1) | (side1 & 0x01);
+ mid2 = (((drflac_uint32)mid2) << 1) | (side2 & 0x01);
+ mid3 = (((drflac_uint32)mid3) << 1) | (side3 & 0x01);
+
+ temp0L = ((mid0 + side0) >> 1);
+ temp1L = ((mid1 + side1) >> 1);
+ temp2L = ((mid2 + side2) >> 1);
+ temp3L = ((mid3 + side3) >> 1);
+
+ temp0R = ((mid0 - side0) >> 1);
+ temp1R = ((mid1 - side1) >> 1);
+ temp2R = ((mid2 - side2) >> 1);
+ temp3R = ((mid3 - side3) >> 1);
+
+ pOutputSamples[i*8+0] = (float)(temp0L * factor);
+ pOutputSamples[i*8+1] = (float)(temp0R * factor);
+ pOutputSamples[i*8+2] = (float)(temp1L * factor);
+ pOutputSamples[i*8+3] = (float)(temp1R * factor);
+ pOutputSamples[i*8+4] = (float)(temp2L * factor);
+ pOutputSamples[i*8+5] = (float)(temp2R * factor);
+ pOutputSamples[i*8+6] = (float)(temp3L * factor);
+ pOutputSamples[i*8+7] = (float)(temp3R * factor);
+ }
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ int mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ int side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)((((mid + side) >> 1) << unusedBitsPerSample) * factor);
+ pOutputSamples[i*2+1] = (float)((((mid - side) >> 1) << unusedBitsPerSample) * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4;
+ float factor;
+ drflac_int32 shift;
+ __m128 factor128;
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor = 1.0f / 8388608.0f;
+ factor128 = _mm_set1_ps(1.0f / 8388608.0f);
+
+ shift = unusedBitsPerSample - 8;
+ if (shift == 0) {
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i mid;
+ __m128i side;
+ __m128i tempL;
+ __m128i tempR;
+ __m128 leftf;
+ __m128 rightf;
+
+ mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
+ tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
+
+ leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
+ rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
+
+ _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
+ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)(((mid + side) >> 1) * factor);
+ pOutputSamples[i*2+1] = (float)(((mid - side) >> 1) * factor);
+ }
+ } else {
+ shift -= 1;
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i mid;
+ __m128i side;
+ __m128i tempL;
+ __m128i tempR;
+ __m128 leftf;
+ __m128 rightf;
+
+ mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
+
+ tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
+ tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
+
+ leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
+ rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
+
+ _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
+ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)(((mid + side) << shift) * factor);
+ pOutputSamples[i*2+1] = (float)(((mid - side) << shift) * factor);
+ }
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4;
+ float factor;
+ drflac_int32 shift;
+ float32x4_t factor4;
+ int32x4_t shift4;
+ int32x4_t wbps0_4; /* Wasted Bits Per Sample */
+ int32x4_t wbps1_4; /* Wasted Bits Per Sample */
+
+ DRFLAC_ASSERT(pFlac->bitsPerSample <= 24);
+
+ frameCount4 = frameCount >> 2;
+
+ factor = 1.0f / 8388608.0f;
+ factor4 = vdupq_n_f32(factor);
+
+ wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ shift = unusedBitsPerSample - 8;
+ if (shift == 0) {
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t lefti;
+ int32x4_t righti;
+ float32x4_t leftf;
+ float32x4_t rightf;
+
+ int32x4_t mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbps0_4);
+ int32x4_t side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbps1_4);
+
+ mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1)));
+
+ lefti = vshrq_n_s32(vaddq_s32(mid, side), 1);
+ righti = vshrq_n_s32(vsubq_s32(mid, side), 1);
+
+ leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
+ rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
+
+ drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)(((mid + side) >> 1) * factor);
+ pOutputSamples[i*2+1] = (float)(((mid - side) >> 1) * factor);
+ }
+ } else {
+ shift -= 1;
+ shift4 = vdupq_n_s32(shift);
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t mid;
+ int32x4_t side;
+ int32x4_t lefti;
+ int32x4_t righti;
+ float32x4_t leftf;
+ float32x4_t rightf;
+
+ mid = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), wbps0_4);
+ side = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), wbps1_4);
+
+ mid = vorrq_s32(vshlq_n_s32(mid, 1), vandq_s32(side, vdupq_n_s32(1)));
+
+ lefti = vshlq_s32(vaddq_s32(mid, side), shift4);
+ righti = vshlq_s32(vsubq_s32(mid, side), shift4);
+
+ leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
+ rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
+
+ drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ drflac_int32 mid = pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
+ drflac_int32 side = pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
+
+ mid = (((drflac_uint32)mid) << 1) | (side & 0x01);
+
+ pOutputSamples[i*2+0] = (float)(((mid + side) << shift) * factor);
+ pOutputSamples[i*2+1] = (float)(((mid - side) << shift) * factor);
+ }
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+#if 0
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ for (drflac_uint64 i = 0; i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1 / 2147483648.0;
+
+ drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
+ drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
+
+ for (i = 0; i < frameCount4; ++i) {
+ drflac_int32 tempL0 = pInputSamples0[i*4+0] << shift0;
+ drflac_int32 tempL1 = pInputSamples0[i*4+1] << shift0;
+ drflac_int32 tempL2 = pInputSamples0[i*4+2] << shift0;
+ drflac_int32 tempL3 = pInputSamples0[i*4+3] << shift0;
+
+ drflac_int32 tempR0 = pInputSamples1[i*4+0] << shift1;
+ drflac_int32 tempR1 = pInputSamples1[i*4+1] << shift1;
+ drflac_int32 tempR2 = pInputSamples1[i*4+2] << shift1;
+ drflac_int32 tempR3 = pInputSamples1[i*4+3] << shift1;
+
+ pOutputSamples[i*8+0] = (float)(tempL0 * factor);
+ pOutputSamples[i*8+1] = (float)(tempR0 * factor);
+ pOutputSamples[i*8+2] = (float)(tempL1 * factor);
+ pOutputSamples[i*8+3] = (float)(tempR1 * factor);
+ pOutputSamples[i*8+4] = (float)(tempL2 * factor);
+ pOutputSamples[i*8+5] = (float)(tempR2 * factor);
+ pOutputSamples[i*8+6] = (float)(tempL3 * factor);
+ pOutputSamples[i*8+7] = (float)(tempR3 * factor);
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor);
+ }
+}
+
+#if defined(DRFLAC_SUPPORT_SSE2)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1.0f / 8388608.0f;
+ __m128 factor128 = _mm_set1_ps(1.0f / 8388608.0f);
+
+ drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
+ drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ for (i = 0; i < frameCount4; ++i) {
+ __m128i lefti;
+ __m128i righti;
+ __m128 leftf;
+ __m128 rightf;
+
+ lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
+ righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
+
+ leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
+ rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
+
+ _mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
+ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor);
+ }
+}
+#endif
+
+#if defined(DRFLAC_SUPPORT_NEON)
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+ drflac_uint64 i;
+ drflac_uint64 frameCount4 = frameCount >> 2;
+
+ float factor = 1.0f / 8388608.0f;
+ float32x4_t factor4 = vdupq_n_f32(factor);
+
+ drflac_int32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
+ drflac_int32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
+
+ int32x4_t shift0_4 = vdupq_n_s32(shift0);
+ int32x4_t shift1_4 = vdupq_n_s32(shift1);
+
+ for (i = 0; i < frameCount4; ++i) {
+ int32x4_t lefti;
+ int32x4_t righti;
+ float32x4_t leftf;
+ float32x4_t rightf;
+
+ lefti = vshlq_s32(vld1q_s32(pInputSamples0 + i*4), shift0_4);
+ righti = vshlq_s32(vld1q_s32(pInputSamples1 + i*4), shift1_4);
+
+ leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
+ rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
+
+ drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
+ }
+
+ for (i = (frameCount4 << 2); i < frameCount; ++i) {
+ pOutputSamples[i*2+0] = (float)((pInputSamples0[i] << shift0) * factor);
+ pOutputSamples[i*2+1] = (float)((pInputSamples1[i] << shift1) * factor);
+ }
+}
+#endif
+
+static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_int32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples)
+{
+#if defined(DRFLAC_SUPPORT_SSE2)
+ if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#elif defined(DRFLAC_SUPPORT_NEON)
+ if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
+ drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+ } else
+#endif
+ {
+ /* Scalar fallback. */
+#if 0
+ drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#else
+ drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
+#endif
+ }
+}
+
+drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut)
+{
+ drflac_uint64 framesRead;
+ drflac_int32 unusedBitsPerSample;
+
+ if (pFlac == NULL || framesToRead == 0) {
+ return 0;
+ }
+
+ if (pBufferOut == NULL) {
+ return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead);
+ }
+
+ unusedBitsPerSample = 32 - pFlac->bitsPerSample;
+
+ framesRead = 0;
+ while (framesToRead > 0) {
+ /* If we've run out of samples in this frame, go to the next. */
+ if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
+ if (!drflac__read_and_decode_next_flac_frame(pFlac)) {
+ break; /* Couldn't read the next frame, so just break from the loop and return. */
+ }
+ } else {
+ unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
+ drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
+ drflac_uint64 frameCountThisIteration = framesToRead;
+
+ if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
+ frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
+ }
+
+ if (channelCount == 2) {
+ const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
+ const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
+
+ switch (pFlac->currentFLACFrame.header.channelAssignment)
+ {
+ case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
+ {
+ drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+
+ case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
+ default:
+ {
+ drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
+ } break;
+ }
+ } else {
+ /* Generic interleaving. */
+ drflac_uint64 i;
+ for (i = 0; i < frameCountThisIteration; ++i) {
+ unsigned int j;
+ for (j = 0; j < channelCount; ++j) {
+ pBufferOut[(i*channelCount)+j] = (float)(((pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)) / 2147483648.0);
+ }
+ }
+ }
+
+ framesRead += frameCountThisIteration;
+ pBufferOut += frameCountThisIteration * channelCount;
+ framesToRead -= frameCountThisIteration;
+ pFlac->currentPCMFrame += frameCountThisIteration;
+ pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
+ }
+ }
+
+ return framesRead;
+}
+
+
+drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex)
+{
+ if (pFlac == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ /* Don't do anything if we're already on the seek point. */
+ if (pFlac->currentPCMFrame == pcmFrameIndex) {
+ return DRFLAC_TRUE;
+ }
+
+ /*
+ 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->firstFLACFramePosInBytes == 0) {
+ return DRFLAC_FALSE;
+ }
+
+ if (pcmFrameIndex == 0) {
+ pFlac->currentPCMFrame = 0;
+ return drflac__seek_to_first_frame(pFlac);
+ } else {
+ drflac_bool32 wasSuccessful = DRFLAC_FALSE;
+
+ /* Clamp the sample to the end. */
+ if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
+ pcmFrameIndex = pFlac->totalPCMFrameCount;
+ }
+
+ /* If the target sample and the current sample are in the same frame we just move the position forward. */
+ if (pcmFrameIndex > pFlac->currentPCMFrame) {
+ /* Forward. */
+ drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
+ if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
+ pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
+ pFlac->currentPCMFrame = pcmFrameIndex;
+ return DRFLAC_TRUE;
+ }
+ } else {
+ /* Backward. */
+ drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
+ drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
+ drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
+ if (currentFLACFramePCMFramesConsumed > offsetAbs) {
+ pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
+ pFlac->currentPCMFrame = pcmFrameIndex;
+ 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_pcm_frame(pFlac, pcmFrameIndex);
+ }
+ else
+#endif
+ {
+ /* First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. */
+ if (!wasSuccessful && !pFlac->_noSeekTableSeek) {
+ wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
+ }
+
+#if !defined(DR_FLAC_NO_CRC)
+ /* Fall back to binary search if seek table seeking fails. This requires the length of the stream to be known. */
+ if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
+ wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
+ }
+#endif
+
+ /* Fall back to brute force if all else fails. */
+ if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
+ wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
+ }
+ }
+
+ pFlac->currentPCMFrame = pcmFrameIndex;
+ return wasSuccessful;
+ }
+}
+
+
+
+/* High Level APIs */
+
+#if defined(SIZE_MAX)
+ #define DRFLAC_SIZE_MAX SIZE_MAX
+#else
+ #if defined(DRFLAC_64BIT)
+ #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRFLAC_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_READ_AND_CLOSE(extension, type) \
+static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\
+{ \
+ type* pSampleData = NULL; \
+ drflac_uint64 totalPCMFrameCount; \
+ \
+ DRFLAC_ASSERT(pFlac != NULL); \
+ \
+ totalPCMFrameCount = pFlac->totalPCMFrameCount; \
+ \
+ if (totalPCMFrameCount == 0) { \
+ type buffer[4096]; \
+ drflac_uint64 pcmFramesRead; \
+ size_t sampleDataBufferSize = sizeof(buffer); \
+ \
+ pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
+ if (pSampleData == NULL) { \
+ goto on_error; \
+ } \
+ \
+ while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
+ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
+ type* pNewSampleData; \
+ size_t newSampleDataBufferSize; \
+ \
+ newSampleDataBufferSize = sampleDataBufferSize * 2; \
+ pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
+ if (pNewSampleData == NULL) { \
+ drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
+ goto on_error; \
+ } \
+ \
+ sampleDataBufferSize = newSampleDataBufferSize; \
+ pSampleData = pNewSampleData; \
+ } \
+ \
+ DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
+ totalPCMFrameCount += pcmFramesRead; \
+ } \
+ \
+ /* 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 + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
+ } else { \
+ drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \
+ if (dataSize > DRFLAC_SIZE_MAX) { \
+ goto on_error; /* The decoded data is too big. */ \
+ } \
+ \
+ pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); /* <-- Safe cast as per the check above. */ \
+ if (pSampleData == NULL) { \
+ goto on_error; \
+ } \
+ \
+ totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \
+ } \
+ \
+ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
+ if (channelsOut) *channelsOut = pFlac->channels; \
+ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
+ \
+ drflac_close(pFlac); \
+ return pSampleData; \
+ \
+on_error: \
+ drflac_close(pFlac); \
+ return NULL; \
+}
+
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32)
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16)
+DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
+
+drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalPCMFrameCountOut) {
+ *totalPCMFrameCountOut = 0;
+ }
+
+ pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
+}
+
+drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalPCMFrameCountOut) {
+ *totalPCMFrameCountOut = 0;
+ }
+
+ pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
+}
+
+float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalPCMFrameCountOut) {
+ *totalPCMFrameCountOut = 0;
+ }
+
+ pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
+}
+
+#ifndef DR_FLAC_NO_STDIO
+drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_file(filename, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_file(filename, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_file(filename, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+#endif
+
+drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ drflac* pFlac;
+
+ if (sampleRate) {
+ *sampleRate = 0;
+ }
+ if (channels) {
+ *channels = 0;
+ }
+ if (totalPCMFrameCount) {
+ *totalPCMFrameCount = 0;
+ }
+
+ pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks);
+ if (pFlac == NULL) {
+ return NULL;
+ }
+
+ return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
+}
+
+
+void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks != NULL) {
+ drflac__free_from_callbacks(p, pAllocationCallbacks);
+ } else {
+ drflac__free_default(p, NULL);
+ }
+}
+
+
+
+
+void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = commentCount;
+ pIter->pRunningData = (const char*)pComments;
+}
+
+const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut)
+{
+ drflac_int32 length;
+ const char* pComment;
+
+ /* Safety. */
+ if (pCommentLengthOut) {
+ *pCommentLengthOut = 0;
+ }
+
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return NULL;
+ }
+
+ length = drflac__le2host_32(*(const drflac_uint32*)pIter->pRunningData);
+ pIter->pRunningData += 4;
+
+ pComment = pIter->pRunningData;
+ pIter->pRunningData += length;
+ pIter->countRemaining -= 1;
+
+ if (pCommentLengthOut) {
+ *pCommentLengthOut = length;
+ }
+
+ return pComment;
+}
+
+
+
+
+void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData)
+{
+ if (pIter == NULL) {
+ return;
+ }
+
+ pIter->countRemaining = trackCount;
+ pIter->pRunningData = (const char*)pTrackData;
+}
+
+drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack)
+{
+ drflac_cuesheet_track cuesheetTrack;
+ const char* pRunningData;
+ drflac_uint64 offsetHi;
+ drflac_uint64 offsetLo;
+
+ if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
+ return DRFLAC_FALSE;
+ }
+
+ pRunningData = pIter->pRunningData;
+
+ offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4;
+ cuesheetTrack.offset = offsetLo | (offsetHi << 32);
+ cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
+ DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
+ cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
+ cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
+ cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
+ cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index);
+
+ pIter->pRunningData = pRunningData;
+ pIter->countRemaining -= 1;
+
+ if (pCuesheetTrack) {
+ *pCuesheetTrack = cuesheetTrack;
+ }
+
+ return DRFLAC_TRUE;
+}
+
+#if defined(__GNUC__)
+ #pragma GCC diagnostic pop
+#endif
+#endif /* DR_FLAC_IMPLEMENTATION */
+
+
+/*
+REVISION HISTORY
+================
+v0.12.2 - 2019-10-07
+ - Internal code clean up.
+
+v0.12.1 - 2019-09-29
+ - Fix some Clang Static Analyzer warnings.
+ - Fix an unused variable warning.
+
+v0.12.0 - 2019-09-23
+ - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation
+ routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:
+ - drflac_open()
+ - drflac_open_relaxed()
+ - drflac_open_with_metadata()
+ - drflac_open_with_metadata_relaxed()
+ - drflac_open_file()
+ - drflac_open_file_with_metadata()
+ - drflac_open_memory()
+ - drflac_open_memory_with_metadata()
+ - drflac_open_and_read_pcm_frames_s32()
+ - drflac_open_and_read_pcm_frames_s16()
+ - drflac_open_and_read_pcm_frames_f32()
+ - drflac_open_file_and_read_pcm_frames_s32()
+ - drflac_open_file_and_read_pcm_frames_s16()
+ - drflac_open_file_and_read_pcm_frames_f32()
+ - drflac_open_memory_and_read_pcm_frames_s32()
+ - drflac_open_memory_and_read_pcm_frames_s16()
+ - drflac_open_memory_and_read_pcm_frames_f32()
+ Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use
+ DRFLAC_MALLOC, DRFLAC_REALLOC and DRFLAC_FREE.
+ - Remove deprecated APIs:
+ - drflac_read_s32()
+ - drflac_read_s16()
+ - drflac_read_f32()
+ - drflac_seek_to_sample()
+ - drflac_open_and_decode_s32()
+ - drflac_open_and_decode_s16()
+ - drflac_open_and_decode_f32()
+ - drflac_open_and_decode_file_s32()
+ - drflac_open_and_decode_file_s16()
+ - drflac_open_and_decode_file_f32()
+ - drflac_open_and_decode_memory_s32()
+ - drflac_open_and_decode_memory_s16()
+ - drflac_open_and_decode_memory_f32()
+ - Remove drflac.totalSampleCount which is now replaced with drflac.totalPCMFrameCount. You can emulate drflac.totalSampleCount
+ by doing pFlac->totalPCMFrameCount*pFlac->channels.
+ - Rename drflac.currentFrame to drflac.currentFLACFrame to remove ambiguity with PCM frames.
+ - Fix errors when seeking to the end of a stream.
+ - Optimizations to seeking.
+ - SSE improvements and optimizations.
+ - ARM NEON optimizations.
+ - Optimizations to drflac_read_pcm_frames_s16().
+ - Optimizations to drflac_read_pcm_frames_s32().
+
+v0.11.10 - 2019-06-26
+ - Fix a compiler error.
+
+v0.11.9 - 2019-06-16
+ - Silence some ThreadSanitizer warnings.
+
+v0.11.8 - 2019-05-21
+ - Fix warnings.
+
+v0.11.7 - 2019-05-06
+ - C89 fixes.
+
+v0.11.6 - 2019-05-05
+ - Add support for C89.
+ - Fix a compiler warning when CRC is disabled.
+ - Change license to choice of public domain or MIT-0.
+
+v0.11.5 - 2019-04-19
+ - Fix a compiler error with GCC.
+
+v0.11.4 - 2019-04-17
+ - Fix some warnings with GCC when compiling with -std=c99.
+
+v0.11.3 - 2019-04-07
+ - Silence warnings with GCC.
+
+v0.11.2 - 2019-03-10
+ - Fix a warning.
+
+v0.11.1 - 2019-02-17
+ - Fix a potential bug with seeking.
+
+v0.11.0 - 2018-12-16
+ - API CHANGE: Deprecated drflac_read_s32(), drflac_read_s16() and drflac_read_f32() and replaced them with
+ drflac_read_pcm_frames_s32(), drflac_read_pcm_frames_s16() and drflac_read_pcm_frames_f32(). The new APIs take
+ and return PCM frame counts instead of sample counts. To upgrade you will need to change the input count by
+ dividing it by the channel count, and then do the same with the return value.
+ - API_CHANGE: Deprecated drflac_seek_to_sample() and replaced with drflac_seek_to_pcm_frame(). Same rules as
+ the changes to drflac_read_*() apply.
+ - API CHANGE: Deprecated drflac_open_and_decode_*() and replaced with drflac_open_*_and_read_*(). Same rules as
+ the changes to drflac_read_*() apply.
+ - Optimizations.
+
+v0.10.0 - 2018-09-11
+ - Remove the DR_FLAC_NO_WIN32_IO option and the Win32 file IO functionality. If you need to use Win32 file IO you
+ need to do it yourself via the callback API.
+ - Fix the clang build.
+ - Fix undefined behavior.
+ - Fix errors with CUESHEET metdata blocks.
+ - Add an API for iterating over each cuesheet track in the CUESHEET metadata block. This works the same way as the
+ Vorbis comment API.
+ - Other miscellaneous bug fixes, mostly relating to invalid FLAC streams.
+ - Minor optimizations.
+
+v0.9.11 - 2018-08-29
+ - Fix a bug with sample reconstruction.
+
+v0.9.10 - 2018-08-07
+ - Improve 64-bit detection.
+
+v0.9.9 - 2018-08-05
+ - Fix C++ build on older versions of GCC.
+
+v0.9.8 - 2018-07-24
+ - Fix compilation errors.
+
+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* and 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 software is available as a choice of the following licenses. Choose
+whichever you prefer.
+
+===============================================================================
+ALTERNATIVE 1 - 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.
+
+For more information, please refer to <http://unlicense.org/>
+
+===============================================================================
+ALTERNATIVE 2 - MIT No Attribution
+===============================================================================
+Copyright 2018 David Reid
+
+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.
+
+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.
+*/
diff --git a/src/libs/decoders/dr_mp3.h b/src/libs/decoders/dr_mp3.h
new file mode 100644
index 000000000..611d652dc
--- /dev/null
+++ b/src/libs/decoders/dr_mp3.h
@@ -0,0 +1,4190 @@
+/*
+MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
+dr_mp3 - v0.5.0 - 2019-10-07
+
+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.
+*/
+
+/*
+RELEASE NOTES - v0.5.0
+=======================
+Version 0.5.0 has breaking API changes.
+
+Improved Client-Defined Memory Allocation
+-----------------------------------------
+The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
+existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are still in place and will be used by default when no custom
+allocation callbacks are specified.
+
+To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() and family, like this:
+
+ void* my_malloc(size_t sz, void* pUserData)
+ {
+ return malloc(sz);
+ }
+ void* my_realloc(void* p, size_t sz, void* pUserData)
+ {
+ return realloc(p, sz);
+ }
+ void my_free(void* p, void* pUserData)
+ {
+ free(p);
+ }
+
+ ...
+
+ drmp3_allocation_callbacks allocationCallbacks;
+ allocationCallbacks.pUserData = &myData;
+ allocationCallbacks.onMalloc = my_malloc;
+ allocationCallbacks.onRealloc = my_realloc;
+ allocationCallbacks.onFree = my_free;
+ drmp3_init_file(&mp3, "my_file.wav", NULL, &allocationCallbacks);
+
+The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
+
+Passing in null for the allocation callbacks object will cause dr_wav to use defaults which is the same as DRMP3_MALLOC,
+DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous versions.
+
+Every API that opens a drmp3 object now takes this extra parameter. These include the following:
+
+ drmp3_init()
+ drmp3_init_file()
+ drmp3_init_memory()
+ drmp3_open_and_read_pcm_frames_f32()
+ drmp3_open_and_read_pcm_frames_s16()
+ drmp3_open_memory_and_read_pcm_frames_f32()
+ drmp3_open_memory_and_read_pcm_frames_s16()
+ drmp3_open_file_and_read_pcm_frames_f32()
+ drmp3_open_file_and_read_pcm_frames_s16()
+
+Renamed APIs
+------------
+The following APIs have been renamed for consistency with other dr_* libraries and to make it clear that they return PCM frame
+counts rather than sample counts.
+
+ drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32()
+ drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16()
+ drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32()
+ drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16()
+ drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32()
+ drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16()
+*/
+
+/*
+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_pcm_frames_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 not 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_pcm_frames_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_read_pcm_frames_f32(), drmp3_open_memory_and_read_pcm_frames_f32() and
+drmp3_open_file_and_read_pcm_frames_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.
+*/
+
+#ifndef dr_mp3_h
+#define dr_mp3_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+
+#if 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 <stdint.h>
+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_PCM_FRAMES_PER_MP3_FRAME 1152
+#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
+
+#ifdef _MSC_VER
+ #define DRMP3_INLINE __forceinline
+#elif defined(__GNUC__)
+ /*
+ I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
+ the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
+ case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
+ command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
+ I am using "__inline__" only when we're compiling in strict ANSI mode.
+ */
+ #if defined(__STRICT_ANSI__)
+ #define DRMP3_INLINE __inline__ __attribute__((always_inline))
+ #else
+ #define DRMP3_INLINE inline __attribute__((always_inline))
+ #endif
+#else
+ #define DRMP3_INLINE
+#endif
+
+/*
+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, void *pcm, drmp3dec_frame_info *info);
+
+/* Helper for converting between f32 and s16. */
+void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples);
+
+
+
+/*
+Main API (Pull API)
+===================
+*/
+#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
+
+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
+ {
+ double alpha;
+ drmp3_bool32 isPrevFramesLoaded : 1;
+ drmp3_bool32 isNextFramesLoaded : 1;
+ } linear;
+ } algo;
+};
+
+typedef enum
+{
+ drmp3_seek_origin_start,
+ drmp3_seek_origin_current
+} drmp3_seek_origin;
+
+typedef struct
+{
+ drmp3_uint64 seekPosInBytes; /* Points to the first byte of an MP3 frame. */
+ drmp3_uint64 pcmFrameIndex; /* The index of the PCM frame this seek point targets. */
+ drmp3_uint16 mp3FramesToDiscard; /* The number of whole MP3 frames to be discarded before pcmFramesToDiscard. */
+ drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */
+} drmp3_seek_point;
+
+/*
+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
+{
+ void* pUserData;
+ void* (* onMalloc)(size_t sz, void* pUserData);
+ void* (* onRealloc)(void* p, size_t sz, void* pUserData);
+ void (* onFree)(void* p, void* pUserData);
+} drmp3_allocation_callbacks;
+
+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_allocation_callbacks allocationCallbacks;
+ drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */
+ drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only. */
+ drmp3_uint32 pcmFramesConsumedInMP3Frame;
+ drmp3_uint32 pcmFramesRemainingInMP3Frame;
+ drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */
+ drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */
+ drmp3_uint64 streamCursor; /* The current byte the decoder is sitting on in the raw stream. */
+ drmp3_src src;
+ drmp3_seek_point* pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */
+ drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */
+ 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, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
+/*
+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, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
+#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, const drmp3_allocation_callbacks* pAllocationCallbacks);
+#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_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
+
+/*
+Reads PCM frames as interleaved signed 16-bit integer PCM.
+
+Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
+*/
+drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
+
+/*
+Seeks to a specific frame.
+
+Note that this is _not_ an MP3 frame, but rather a PCM frame.
+*/
+drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
+
+/*
+Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+*/
+drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
+
+/*
+Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+*/
+drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
+
+/*
+Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
+radio. Runs in linear time. Returns 0 on error.
+
+This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except that it's more efficient.
+*/
+drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
+
+/*
+Calculates the seekpoints based on PCM frames. This is slow.
+
+pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains the desired count.
+On output it contains the actual count. The reason for this design is that the client may request too many
+seekpoints, in which case dr_mp3 will return a corrected count.
+
+Note that seektable seeking is not quite sample exact when the MP3 stream contains inconsistent sample rates.
+*/
+drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints);
+
+/*
+Binds a seek table to the decoder.
+
+This does _not_ make a copy of pSeekPoints - it only references it. It is up to the application to ensure this
+remains valid while it is bound to the decoder.
+
+Use drmp3_calculate_seek_points() to calculate the seek points.
+*/
+drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints);
+
+
+/*
+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_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
+float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
+#ifndef DR_MP3_NO_STDIO
+float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
+#endif
+
+/*
+Frees any memory that was allocated by a public drmp3 API.
+*/
+void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* dr_mp3_h */
+
+
+/************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************
+
+ IMPLEMENTATION
+
+ ************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************/
+#ifdef DR_MP3_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h> /* For INT_MAX */
+
+/* Disable SIMD when compiling with TCC for now. */
+#if defined(__TINYC__)
+#define DR_MP3_NO_SIMD
+#endif
+
+#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset)))
+
+#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 /* more than ISO spec's */
+#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES
+#define DRMP3_MAX_FRAME_SYNC_MATCHES 10
+#endif
+
+#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) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
+#if defined(_MSC_VER)
+#include <intrin.h>
+#endif
+#include <emmintrin.h>
+#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 <arm_neon.h>
+#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 gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
+ int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
+
+ 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 drmp3_int16 *codebook = tabs + tabindex[tab_num];
+ int linbits = g_linbits[tab_num];
+ if (linbits)
+ {
+ 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)
+ {
+ lsb += DRMP3_PEEK_BITS(linbits);
+ DRMP3_FLUSH_BITS(linbits);
+ DRMP3_CHECK_BITS;
+ *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)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);
+ } else
+ {
+ 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;
+ *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
+}
+
+#ifndef DR_MP3_FLOAT_OUTPUT
+typedef drmp3_int16 drmp3d_sample_t;
+
+static drmp3_int16 drmp3d_scale_pcm(float sample)
+{
+ drmp3_int16 s;
+ if (sample >= 32766.5) return (drmp3_int16) 32767;
+ if (sample <= -32767.5) return (drmp3_int16)-32768;
+ s = (drmp3_int16)(sample + .5f);
+ s -= (s < 0); /* away from zero, to be compliant */
+ return (drmp3_int16)s;
+}
+#else
+typedef float drmp3d_sample_t;
+
+static float drmp3d_scale_pcm(float sample)
+{
+ return sample*(1.f/32768.f);
+}
+#endif
+
+static void drmp3d_synth_pair(drmp3d_sample_t *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, drmp3d_sample_t *dstl, int nch, float *lins)
+{
+ int i;
+ float *xr = xl + 576*(nch - 1);
+ drmp3d_sample_t *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)
+
+ {
+#ifndef DR_MP3_FLOAT_OUTPUT
+#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] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
+ dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
+ dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
+ dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
+ dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
+ dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
+ dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
+ dstl[(49 + i)*nch] = (drmp3_int16)_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
+ static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
+ a = DRMP3_VMUL(a, g_scale);
+ b = DRMP3_VMUL(b, g_scale);
+#if DRMP3_HAVE_SSE
+ _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
+ _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
+ _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
+ _mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
+ _mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
+ _mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
+ _mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
+ _mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
+#else
+ vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
+ vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
+ vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
+ vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
+ vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
+ vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
+ vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
+ vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
+#endif
+#endif /* DR_MP3_FLOAT_OUTPUT */
+ }
+ } 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, drmp3d_sample_t *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, void *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 && pcm != NULL)
+ {
+ for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*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, (drmp3d_sample_t*)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];
+
+ if (pcm == NULL) {
+ return drmp3_hdr_frame_samples(hdr);
+ }
+
+ 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, (drmp3d_sample_t*)pcm, scratch.syn[0]);
+ memset(scratch.grbuf[0], 0, 576*2*sizeof(float));
+ pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
+ }
+ if (bs_frame->pos > bs_frame->limit)
+ {
+ drmp3dec_init(dec);
+ return 0;
+ }
+ }
+#endif
+ }
+
+ return success*drmp3_hdr_frame_samples(dec->header);
+}
+
+void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
+{
+ if(num_samples > 0)
+ {
+ int i = 0;
+#if DRMP3_HAVE_SIMD
+ int aligned_count = num_samples & ~7;
+ for(; i < aligned_count; i+=8)
+ {
+ drmp3_f4 scale = DRMP3_VSET(32768.0f);
+ drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale);
+ drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale);
+#if DRMP3_HAVE_SSE
+ drmp3_f4 s16max = DRMP3_VSET( 32767.0f);
+ drmp3_f4 s16min = DRMP3_VSET(-32768.0f);
+ __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
+ _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
+ out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
+ out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
+ out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
+ out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
+ out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
+ out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
+ out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
+ out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
+#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(out+i , pcma, 0);
+ vst1_lane_s16(out+i+1, pcma, 1);
+ vst1_lane_s16(out+i+2, pcma, 2);
+ vst1_lane_s16(out+i+3, pcma, 3);
+ vst1_lane_s16(out+i+4, pcmb, 0);
+ vst1_lane_s16(out+i+5, pcmb, 1);
+ vst1_lane_s16(out+i+6, pcmb, 2);
+ vst1_lane_s16(out+i+7, pcmb, 3);
+#endif
+ }
+#endif
+ for(; i < num_samples; i++)
+ {
+ float sample = in[i] * 32768.0f;
+ if (sample >= 32766.5)
+ out[i] = (drmp3_int16) 32767;
+ else if (sample <= -32767.5)
+ out[i] = (drmp3_int16)-32768;
+ else
+ {
+ short s = (drmp3_int16)(sample + .5f);
+ s -= (s < 0); /* away from zero, to be compliant */
+ out[i] = s;
+ }
+ }
+ }
+}
+
+
+
+/************************************************************************************************************************************************************
+
+ Main Public API
+
+ ************************************************************************************************************************************************************/
+
+#if defined(SIZE_MAX)
+ #define DRMP3_SIZE_MAX SIZE_MAX
+#else
+ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+ #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRMP3_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
+/* Options. */
+#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES
+#define DRMP3_SEEK_LEADING_MP3_FRAMES 2
+#endif
+
+
+/* Standard library stuff. */
+#ifndef DRMP3_ASSERT
+#include <assert.h>
+#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_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 DRMP3_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)
+{
+ drmp3_uint32 i;
+ for (i = 0; i < channels; ++i) {
+ pOut[i] = drmp3_mix_f32(pInA[i], pInB[i], factor);
+ }
+}
+
+
+static void* drmp3__malloc_default(size_t sz, void* pUserData)
+{
+ (void)pUserData;
+ return DRMP3_MALLOC(sz);
+}
+
+static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData)
+{
+ (void)pUserData;
+ return DRMP3_REALLOC(p, sz);
+}
+
+static void drmp3__free_default(void* p, void* pUserData)
+{
+ (void)pUserData;
+ DRMP3_FREE(p);
+}
+
+
+static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks == NULL) {
+ return NULL;
+ }
+
+ if (pAllocationCallbacks->onMalloc != NULL) {
+ return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
+ }
+
+ /* Try using realloc(). */
+ if (pAllocationCallbacks->onRealloc != NULL) {
+ return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
+ }
+
+ return NULL;
+}
+
+static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks == NULL) {
+ return NULL;
+ }
+
+ if (pAllocationCallbacks->onRealloc != NULL) {
+ return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
+ }
+
+ /* Try emulating realloc() in terms of malloc()/free(). */
+ if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
+ void* p2;
+
+ p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
+ if (p2 == NULL) {
+ return NULL;
+ }
+
+ DRMP3_COPY_MEMORY(p2, p, szOld);
+ pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
+
+ return p2;
+ }
+
+ return NULL;
+}
+
+static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ if (p == NULL || pAllocationCallbacks == NULL) {
+ return;
+ }
+
+ if (pAllocationCallbacks->onFree != NULL) {
+ pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
+ }
+}
+
+
+drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks != NULL) {
+ /* Copy. */
+ return *pAllocationCallbacks;
+ } else {
+ /* Defaults. */
+ drmp3_allocation_callbacks allocationCallbacks;
+ allocationCallbacks.pUserData = NULL;
+ allocationCallbacks.onMalloc = drmp3__malloc_default;
+ allocationCallbacks.onRealloc = drmp3__realloc_default;
+ allocationCallbacks.onFree = drmp3__free_default;
+ return allocationCallbacks;
+ }
+}
+
+
+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_uint32 channels;
+ drmp3_uint64 totalFramesRead = 0;
+
+ DRMP3_ASSERT(pCache != NULL);
+ DRMP3_ASSERT(pCache->pSRC != NULL);
+ DRMP3_ASSERT(pCache->pSRC->onRead != NULL);
+ DRMP3_ASSERT(frameCount > 0);
+ DRMP3_ASSERT(pFramesOut != NULL);
+
+ channels = pCache->pSRC->config.channels;
+
+ while (frameCount > 0) {
+ /* If there's anything in memory go ahead and copy that over first. */
+ drmp3_uint32 framesToReadFromClient;
+ 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;
+
+ 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;
+ }
+
+ pSRC->config = *pConfig;
+ 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)
+{
+ drmp3_src_algorithm algorithm;
+
+ if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) {
+ return 0;
+ }
+
+ 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)
+{
+ double factor;
+ drmp3_uint64 totalFramesRead;
+
+ 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;
+ }
+
+ factor = (double)pSRC->config.sampleRateIn / pSRC->config.sampleRateOut;
+
+ totalFramesRead = 0;
+ while (frameCount > 0) {
+ drmp3_uint32 i;
+ drmp3_uint32 framesToReadFromClient;
+
+ /* 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, (float)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. */
+ framesToReadFromClient = (drmp3_uint32)pSRC->algo.linear.alpha;
+ pSRC->algo.linear.alpha = pSRC->algo.linear.alpha - framesToReadFromClient;
+
+ for (i = 0; i < framesToReadFromClient; ++i) {
+ drmp3_uint64 framesRead;
+ drmp3_uint32 j;
+
+ for (j = 0; j < pSRC->config.channels; ++j) {
+ pPrevFrame[j] = pNextFrame[j];
+ }
+
+ framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pNextFrame);
+ if (framesRead == 0) {
+ drmp3_uint32 k;
+ for (k = 0; k < pSRC->config.channels; ++k) {
+ pNextFrame[k] = 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 size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
+{
+ size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
+ pMP3->streamCursor += bytesRead;
+ return bytesRead;
+}
+
+static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
+{
+ DRMP3_ASSERT(offset >= 0);
+
+ if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
+ return DRMP3_FALSE;
+ }
+
+ if (origin == drmp3_seek_origin_start) {
+ pMP3->streamCursor = (drmp3_uint64)offset;
+ } else {
+ pMP3->streamCursor += offset;
+ }
+
+ return DRMP3_TRUE;
+}
+
+static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin)
+{
+ if (offset <= 0x7FFFFFFF) {
+ return drmp3__on_seek(pMP3, (int)offset, origin);
+ }
+
+
+ /* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */
+ if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
+ return DRMP3_FALSE;
+ }
+
+ offset -= 0x7FFFFFFF;
+ while (offset > 0) {
+ if (offset <= 0x7FFFFFFF) {
+ if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
+ return DRMP3_FALSE;
+ }
+ offset = 0;
+ } else {
+ if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
+ return DRMP3_FALSE;
+ }
+ offset -= 0x7FFFFFFF;
+ }
+ }
+
+ return DRMP3_TRUE;
+}
+
+static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3_bool32 discard);
+static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3);
+
+static drmp3_uint64 drmp3_read_src(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData)
+{
+ drmp3* pMP3 = (drmp3*)pUserData;
+ float* pFramesOutF = (float*)pFramesOut;
+ drmp3_uint64 totalFramesRead = 0;
+
+ DRMP3_ASSERT(pMP3 != NULL);
+ DRMP3_ASSERT(pMP3->onRead != NULL);
+
+ while (frameCount > 0) {
+ /* Read from the in-memory buffer first. */
+ while (pMP3->pcmFramesRemainingInMP3Frame > 0 && frameCount > 0) {
+ drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->pcmFrames;
+#ifndef DR_MP3_FLOAT_OUTPUT
+ if (pMP3->mp3FrameChannels == 1) {
+ if (pMP3->channels == 1) {
+ /* Mono -> Mono. */
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
+ } else {
+ /* Mono -> Stereo. */
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
+ pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame] / 32768.0f;
+ }
+ } else {
+ if (pMP3->channels == 1) {
+ /* Stereo -> Mono */
+ float sample = 0;
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0] / 32768.0f;
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1] / 32768.0f;
+ pFramesOutF[0] = sample * 0.5f;
+ } else {
+ /* Stereo -> Stereo */
+ pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0] / 32768.0f;
+ pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1] / 32768.0f;
+ }
+ }
+#else
+ if (pMP3->mp3FrameChannels == 1) {
+ if (pMP3->channels == 1) {
+ /* Mono -> Mono. */
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame];
+ } else {
+ /* Mono -> Stereo. */
+ pFramesOutF[0] = frames[pMP3->pcmFramesConsumedInMP3Frame];
+ pFramesOutF[1] = frames[pMP3->pcmFramesConsumedInMP3Frame];
+ }
+ } else {
+ if (pMP3->channels == 1) {
+ /* Stereo -> Mono */
+ float sample = 0;
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0];
+ sample += frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1];
+ pFramesOutF[0] = sample * 0.5f;
+ } else {
+ /* Stereo -> Stereo */
+ pFramesOutF[0] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+0];
+ pFramesOutF[1] = frames[(pMP3->pcmFramesConsumedInMP3Frame*pMP3->mp3FrameChannels)+1];
+ }
+ }
+#endif
+
+ pMP3->pcmFramesConsumedInMP3Frame += 1;
+ pMP3->pcmFramesRemainingInMP3Frame -= 1;
+ totalFramesRead += 1;
+ frameCount -= 1;
+ pFramesOutF += pSRC->config.channels;
+ }
+
+ if (frameCount == 0) {
+ break;
+ }
+
+ DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 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) == 0) {
+ break;
+ }
+ }
+
+ return totalFramesRead;
+}
+
+static drmp3_bool32 drmp3_init_src(drmp3* pMP3)
+{
+ 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)) {
+ drmp3_uninit(pMP3);
+ return DRMP3_FALSE;
+ }
+
+ return DRMP3_TRUE;
+}
+
+static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames, drmp3_bool32 discard)
+{
+ drmp3_uint32 pcmFramesRead = 0;
+
+ DRMP3_ASSERT(pMP3 != NULL);
+ DRMP3_ASSERT(pMP3->onRead != NULL);
+
+ if (pMP3->atEnd) {
+ return 0;
+ }
+
+ do {
+ drmp3dec_frame_info info;
+ size_t leftoverDataSize;
+
+ /* 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) {
+ size_t bytesRead;
+
+ if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
+ drmp3_uint8* pNewData;
+ size_t newDataCap;
+
+ newDataCap = DRMP3_DATA_CHUNK_SIZE;
+
+ pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
+ if (pNewData == NULL) {
+ return 0; /* Out of memory. */
+ }
+
+ pMP3->pData = pNewData;
+ pMP3->dataCapacity = newDataCap;
+ }
+
+ bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+ if (bytesRead == 0) {
+ if (pMP3->dataSize == 0) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; /* No data. */
+ }
+ }
+
+ pMP3->dataSize += bytesRead;
+ }
+
+ if (pMP3->dataSize > INT_MAX) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; /* File too big. */
+ }
+
+ pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */
+
+ /* Consume the data. */
+ leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
+ if (info.frame_bytes > 0) {
+ memmove(pMP3->pData, pMP3->pData + info.frame_bytes, leftoverDataSize);
+ pMP3->dataSize = leftoverDataSize;
+ }
+
+ /*
+ pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully
+ decoded the frame. A special case is if we are wanting to discard the frame, in which case we return successfully.
+ */
+ if (pcmFramesRead > 0 || (info.frame_bytes > 0 && discard)) {
+ pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
+ pMP3->pcmFramesConsumedInMP3Frame = 0;
+ pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
+ pMP3->mp3FrameChannels = info.channels;
+ pMP3->mp3FrameSampleRate = info.hz;
+
+ /* We need to initialize the resampler if we don't yet have the channel count or sample rate. */
+ if (pMP3->channels == 0 || pMP3->sampleRate == 0) {
+ if (pMP3->channels == 0) {
+ pMP3->channels = info.channels;
+ }
+ if (pMP3->sampleRate == 0) {
+ pMP3->sampleRate = info.hz;
+ }
+ drmp3_init_src(pMP3);
+ }
+
+ drmp3_src_set_input_sample_rate(&pMP3->src, pMP3->mp3FrameSampleRate);
+ break;
+ } else if (info.frame_bytes == 0) {
+ size_t bytesRead;
+
+ /* Need more data. minimp3 recommends doing data submission in 16K chunks. */
+ if (pMP3->dataCapacity == pMP3->dataSize) {
+ /* No room. Expand. */
+ drmp3_uint8* pNewData;
+ size_t newDataCap;
+
+ newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE;
+
+ pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
+ if (pNewData == NULL) {
+ return 0; /* Out of memory. */
+ }
+
+ pMP3->pData = pNewData;
+ pMP3->dataCapacity = newDataCap;
+ }
+
+ /* Fill in a chunk. */
+ bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
+ if (bytesRead == 0) {
+ pMP3->atEnd = DRMP3_TRUE;
+ return 0; /* Error reading more data. */
+ }
+
+ pMP3->dataSize += bytesRead;
+ }
+ } while (DRMP3_TRUE);
+
+ return pcmFramesRead;
+}
+
+static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
+{
+ DRMP3_ASSERT(pMP3 != NULL);
+ return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames, DRMP3_FALSE);
+}
+
+#if 0
+static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
+{
+ drmp3_uint32 pcmFrameCount;
+
+ DRMP3_ASSERT(pMP3 != NULL);
+
+ pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
+ if (pcmFrameCount == 0) {
+ return 0;
+ }
+
+ /* We have essentially just skipped past the frame, so just set the remaining samples to 0. */
+ pMP3->currentPCMFrame += pcmFrameCount;
+ pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+
+ return pcmFrameCount;
+}
+#endif
+
+drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_config* pConfig, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ drmp3_config config;
+
+ 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. */
+ if (pConfig != NULL) {
+ config = *pConfig;
+ } else {
+ DRMP3_ZERO_OBJECT(&config);
+ }
+
+ pMP3->channels = config.outputChannels;
+
+ /* Cannot have more than 2 channels. */
+ if (pMP3->channels > 2) {
+ pMP3->channels = 2;
+ }
+
+ pMP3->sampleRate = config.outputSampleRate;
+
+ pMP3->onRead = onRead;
+ pMP3->onSeek = onSeek;
+ pMP3->pUserData = pUserData;
+ pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
+
+ if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
+ return DRMP3_FALSE; /* Invalid allocation callbacks. */
+ }
+
+ /*
+ We need a sample rate converter for converting the sample rate from the MP3 frames to the requested output sample rate. Note that if
+ we don't yet know the channel count or sample rate we defer this until the first frame is read.
+ */
+ if (pMP3->channels != 0 && pMP3->sampleRate != 0) {
+ drmp3_init_src(pMP3);
+ }
+
+ /* Decode the first frame to confirm that it is indeed a valid MP3 stream. */
+ if (!drmp3_decode_next_frame(pMP3)) {
+ drmp3_uninit(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, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pMP3 == NULL || onRead == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ DRMP3_ZERO_OBJECT(pMP3);
+ return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pConfig, pAllocationCallbacks);
+}
+
+
+static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ drmp3* pMP3 = (drmp3*)pUserData;
+ size_t bytesRemaining;
+
+ DRMP3_ASSERT(pMP3 != NULL);
+ DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
+
+ 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, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ 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, pAllocationCallbacks);
+}
+
+
+#ifndef DR_MP3_NO_STDIO
+#include <stdio.h>
+
+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, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ 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, pAllocationCallbacks);
+}
+#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_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
+}
+
+drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
+{
+ drmp3_uint64 totalFramesRead = 0;
+
+ if (pMP3 == NULL || pMP3->onRead == NULL) {
+ return 0;
+ }
+
+ if (pBufferOut == NULL) {
+ float temp[4096];
+ while (framesToRead > 0) {
+ drmp3_uint64 framesJustRead;
+ drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels;
+ if (framesToReadRightNow > framesToRead) {
+ framesToReadRightNow = framesToRead;
+ }
+
+ framesJustRead = drmp3_read_pcm_frames_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);
+ pMP3->currentPCMFrame += totalFramesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
+{
+ float tempF32[4096];
+ drmp3_uint64 pcmFramesJustRead;
+ drmp3_uint64 totalPCMFramesRead = 0;
+
+ if (pMP3 == NULL || pMP3->onRead == NULL) {
+ return 0;
+ }
+
+ /* Naive implementation: read into a temp f32 buffer, then convert. */
+ for (;;) {
+ drmp3_uint64 pcmFramesToReadThisIteration = (framesToRead - totalPCMFramesRead);
+ if (pcmFramesToReadThisIteration > drmp3_countof(tempF32)/pMP3->channels) {
+ pcmFramesToReadThisIteration = drmp3_countof(tempF32)/pMP3->channels;
+ }
+
+ pcmFramesJustRead = drmp3_read_pcm_frames_f32(pMP3, pcmFramesToReadThisIteration, tempF32);
+ if (pcmFramesJustRead == 0) {
+ break;
+ }
+
+ drmp3dec_f32_to_s16(tempF32, pBufferOut, (int)(pcmFramesJustRead * pMP3->channels)); /* <-- Safe cast since pcmFramesJustRead will be clamped based on the size of tempF32 which is always small. */
+ pBufferOut += pcmFramesJustRead * pMP3->channels;
+
+ totalPCMFramesRead += pcmFramesJustRead;
+
+ if (pcmFramesJustRead < pcmFramesToReadThisIteration) {
+ break;
+ }
+ }
+
+ return totalPCMFramesRead;
+}
+
+void drmp3_reset(drmp3* pMP3)
+{
+ DRMP3_ASSERT(pMP3 != NULL);
+
+ pMP3->pcmFramesConsumedInMP3Frame = 0;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+ pMP3->currentPCMFrame = 0;
+ pMP3->dataSize = 0;
+ pMP3->atEnd = DRMP3_FALSE;
+ pMP3->src.bin[0] = 0;
+ pMP3->src.bin[1] = 0;
+ pMP3->src.bin[2] = 0;
+ pMP3->src.bin[3] = 0;
+ pMP3->src.cache.cachedFrameCount = 0;
+ pMP3->src.cache.iNextFrame = 0;
+ pMP3->src.algo.linear.alpha = 0;
+ pMP3->src.algo.linear.isNextFramesLoaded = 0;
+ pMP3->src.algo.linear.isPrevFramesLoaded = 0;
+ drmp3dec_init(&pMP3->decoder);
+}
+
+drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
+{
+ DRMP3_ASSERT(pMP3 != NULL);
+ DRMP3_ASSERT(pMP3->onSeek != NULL);
+
+ /* Seek to the start of the stream to begin with. */
+ if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
+ return DRMP3_FALSE;
+ }
+
+ /* Clear any cached data. */
+ drmp3_reset(pMP3);
+ return DRMP3_TRUE;
+}
+
+float drmp3_get_cached_pcm_frame_count_from_src(drmp3* pMP3)
+{
+ return (pMP3->src.cache.cachedFrameCount - pMP3->src.cache.iNextFrame) + (float)pMP3->src.algo.linear.alpha;
+}
+
+float drmp3_get_pcm_frames_remaining_in_mp3_frame(drmp3* pMP3)
+{
+ float factor = (float)pMP3->src.config.sampleRateOut / (float)pMP3->src.config.sampleRateIn;
+ float frameCountPreSRC = drmp3_get_cached_pcm_frame_count_from_src(pMP3) + pMP3->pcmFramesRemainingInMP3Frame;
+ return frameCountPreSRC * factor;
+}
+
+/*
+NOTE ON SEEKING
+===============
+The seeking code below is a complete mess and is broken for cases when the sample rate changes. The problem
+is with the resampling and the crappy resampler used by dr_mp3. What needs to happen is the following:
+
+1) The resampler needs to be replaced.
+2) The resampler has state which needs to be updated whenever an MP3 frame is decoded outside of
+ drmp3_read_pcm_frames_f32(). The resampler needs an API to "flush" some imaginary input so that it's
+ state is updated accordingly.
+*/
+drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
+{
+ drmp3_uint64 framesRead;
+
+#if 0
+ /*
+ MP3 is a bit annoying when it comes to seeking because of the bit reservoir. It basically means that an MP3 frame can possibly
+ depend on some of the data of prior frames. This means it's not as simple as seeking to the first byte of the MP3 frame that
+ contains the sample because that MP3 frame will need the data from the previous MP3 frame (which we just seeked past!). To
+ resolve this we seek past a number of MP3 frames up to a point, and then read-and-discard the remainder.
+ */
+ drmp3_uint64 maxFramesToReadAndDiscard = (drmp3_uint64)(DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME * 3 * ((float)pMP3->src.config.sampleRateOut / (float)pMP3->src.config.sampleRateIn));
+
+ /* Now get rid of leading whole frames. */
+ while (frameOffset > maxFramesToReadAndDiscard) {
+ float pcmFramesRemainingInCurrentMP3FrameF = drmp3_get_pcm_frames_remaining_in_mp3_frame(pMP3);
+ drmp3_uint32 pcmFramesRemainingInCurrentMP3Frame = (drmp3_uint32)pcmFramesRemainingInCurrentMP3FrameF;
+ if (frameOffset > pcmFramesRemainingInCurrentMP3Frame) {
+ frameOffset -= pcmFramesRemainingInCurrentMP3Frame;
+ pMP3->currentPCMFrame += pcmFramesRemainingInCurrentMP3Frame;
+ pMP3->pcmFramesConsumedInMP3Frame += pMP3->pcmFramesRemainingInMP3Frame;
+ pMP3->pcmFramesRemainingInMP3Frame = 0;
+ } else {
+ break;
+ }
+
+ drmp3_uint32 pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, pMP3->pcmFrames, DRMP3_FALSE);
+ if (pcmFrameCount == 0) {
+ break;
+ }
+ }
+
+ /* The last step is to read-and-discard any remaining PCM frames to make it sample-exact. */
+ framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
+ if (framesRead != frameOffset) {
+ return DRMP3_FALSE;
+ }
+#else
+ /* Just using a dumb read-and-discard for now pending updates to the resampler. */
+ framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
+ if (framesRead != frameOffset) {
+ return DRMP3_FALSE;
+ }
+#endif
+
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
+{
+ DRMP3_ASSERT(pMP3 != NULL);
+
+ if (frameIndex == pMP3->currentPCMFrame) {
+ return DRMP3_TRUE;
+ }
+
+ /*
+ If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of
+ the stream and read from the beginning.
+ */
+ if (frameIndex < pMP3->currentPCMFrame) {
+ /* Moving backward. Move to the start of the stream and then move forward. */
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+ }
+
+ DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
+ return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
+}
+
+drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
+{
+ drmp3_uint32 iSeekPoint;
+
+ DRMP3_ASSERT(pSeekPointIndex != NULL);
+
+ *pSeekPointIndex = 0;
+
+ if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
+ return DRMP3_FALSE;
+ }
+
+ /* Linear search for simplicity to begin with while I'm getting this thing working. Once it's all working change this to a binary search. */
+ for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
+ if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
+ break; /* Found it. */
+ }
+
+ *pSeekPointIndex = iSeekPoint;
+ }
+
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
+{
+ drmp3_seek_point seekPoint;
+ drmp3_uint32 priorSeekPointIndex;
+ drmp3_uint16 iMP3Frame;
+ drmp3_uint64 leftoverFrames;
+
+ DRMP3_ASSERT(pMP3 != NULL);
+ DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
+ DRMP3_ASSERT(pMP3->seekPointCount > 0);
+
+ /* If there is no prior seekpoint it means the target PCM frame comes before the first seek point. Just assume a seekpoint at the start of the file in this case. */
+ if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
+ seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
+ } else {
+ seekPoint.seekPosInBytes = 0;
+ seekPoint.pcmFrameIndex = 0;
+ seekPoint.mp3FramesToDiscard = 0;
+ seekPoint.pcmFramesToDiscard = 0;
+ }
+
+ /* First thing to do is seek to the first byte of the relevant MP3 frame. */
+ if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
+ return DRMP3_FALSE; /* Failed to seek. */
+ }
+
+ /* Clear any cached data. */
+ drmp3_reset(pMP3);
+
+ /* Whole MP3 frames need to be discarded first. */
+ for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
+ drmp3_uint32 pcmFramesReadPreSRC;
+ drmp3d_sample_t* pPCMFrames;
+
+ /* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */
+ pPCMFrames = NULL;
+ if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
+ pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames;
+ }
+
+ /* We first need to decode the next frame, and then we need to flush the resampler. */
+ pcmFramesReadPreSRC = drmp3_decode_next_frame_ex(pMP3, pPCMFrames, DRMP3_TRUE);
+ if (pcmFramesReadPreSRC == 0) {
+ return DRMP3_FALSE;
+ }
+ }
+
+ /* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */
+ pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
+
+ /*
+ Update resampler. This is wrong. Need to instead update it on a per MP3 frame basis. Also broken for cases when
+ the sample rate is being reduced in my testing. Should work fine when the input and output sample rate is the same
+ or a clean multiple.
+ */
+ pMP3->src.algo.linear.alpha = (drmp3_int64)pMP3->currentPCMFrame * ((double)pMP3->src.config.sampleRateIn / pMP3->src.config.sampleRateOut); /* <-- Cast to int64 is required for VC6. */
+ pMP3->src.algo.linear.alpha = pMP3->src.algo.linear.alpha - (drmp3_uint32)(pMP3->src.algo.linear.alpha);
+ if (pMP3->src.algo.linear.alpha > 0) {
+ pMP3->src.algo.linear.isPrevFramesLoaded = 1;
+ }
+
+ /*
+ Now at this point we can follow the same process as the brute force technique where we just skip over unnecessary MP3 frames and then
+ read-and-discard at least 2 whole MP3 frames.
+ */
+ leftoverFrames = frameIndex - pMP3->currentPCMFrame;
+ return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
+}
+
+drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
+{
+ if (pMP3 == NULL || pMP3->onSeek == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ if (frameIndex == 0) {
+ return drmp3_seek_to_start_of_stream(pMP3);
+ }
+
+ /* Use the seek table if we have one. */
+ if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
+ return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
+ } else {
+ return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
+ }
+}
+
+drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount)
+{
+ drmp3_uint64 currentPCMFrame;
+ drmp3_uint64 totalPCMFrameCount;
+ drmp3_uint64 totalMP3FrameCount;
+ float totalPCMFrameCountFractionalPart;
+
+ if (pMP3 == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ /*
+ The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based
+ on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function.
+ */
+
+ /* The stream must support seeking for this to work. */
+ if (pMP3->onSeek == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ /* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */
+ currentPCMFrame = pMP3->currentPCMFrame;
+
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+
+ totalPCMFrameCount = 0;
+ totalMP3FrameCount = 0;
+
+ totalPCMFrameCountFractionalPart = 0; /* <-- With resampling there will be a fractional part to each MP3 frame that we need to accumulate. */
+ for (;;) {
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
+ float srcRatio;
+ float pcmFramesInCurrentMP3FrameOutF;
+ drmp3_uint32 pcmFramesInCurrentMP3FrameOut;
+
+ pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, DRMP3_FALSE);
+ if (pcmFramesInCurrentMP3FrameIn == 0) {
+ break;
+ }
+
+ srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
+ DRMP3_ASSERT(srcRatio > 0);
+
+ pcmFramesInCurrentMP3FrameOutF = totalPCMFrameCountFractionalPart + (pcmFramesInCurrentMP3FrameIn / srcRatio);
+ pcmFramesInCurrentMP3FrameOut = (drmp3_uint32)pcmFramesInCurrentMP3FrameOutF;
+ totalPCMFrameCountFractionalPart = pcmFramesInCurrentMP3FrameOutF - pcmFramesInCurrentMP3FrameOut;
+ totalPCMFrameCount += pcmFramesInCurrentMP3FrameOut;
+ totalMP3FrameCount += 1;
+ }
+
+ /* Finally, we need to seek back to where we were. */
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+
+ if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
+ return DRMP3_FALSE;
+ }
+
+ if (pMP3FrameCount != NULL) {
+ *pMP3FrameCount = totalMP3FrameCount;
+ }
+ if (pPCMFrameCount != NULL) {
+ *pPCMFrameCount = totalPCMFrameCount;
+ }
+
+ return DRMP3_TRUE;
+}
+
+drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
+{
+ drmp3_uint64 totalPCMFrameCount;
+ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
+ return 0;
+ }
+
+ return totalPCMFrameCount;
+}
+
+drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
+{
+ drmp3_uint64 totalMP3FrameCount;
+ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
+ return 0;
+ }
+
+ return totalMP3FrameCount;
+}
+
+void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
+{
+ float srcRatio;
+ float pcmFrameCountOutF;
+ drmp3_uint32 pcmFrameCountOut;
+
+ srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
+ DRMP3_ASSERT(srcRatio > 0);
+
+ pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
+ pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF;
+ *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
+ *pRunningPCMFrameCount += pcmFrameCountOut;
+}
+
+typedef struct
+{
+ drmp3_uint64 bytePos;
+ drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */
+} drmp3__seeking_mp3_frame_info;
+
+drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints)
+{
+ drmp3_uint32 seekPointCount;
+ drmp3_uint64 currentPCMFrame;
+ drmp3_uint64 totalMP3FrameCount;
+ drmp3_uint64 totalPCMFrameCount;
+
+ if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
+ return DRMP3_FALSE; /* Invalid args. */
+ }
+
+ seekPointCount = *pSeekPointCount;
+ if (seekPointCount == 0) {
+ return DRMP3_FALSE; /* The client has requested no seek points. Consider this to be invalid arguments since the client has probably not intended this. */
+ }
+
+ /* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */
+ currentPCMFrame = pMP3->currentPCMFrame;
+
+ /* We never do more than the total number of MP3 frames and we limit it to 32-bits. */
+ if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
+ return DRMP3_FALSE;
+ }
+
+ /* If there's less than DRMP3_SEEK_LEADING_MP3_FRAMES+1 frames we just report 1 seek point which will be the very start of the stream. */
+ if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) {
+ seekPointCount = 1;
+ pSeekPoints[0].seekPosInBytes = 0;
+ pSeekPoints[0].pcmFrameIndex = 0;
+ pSeekPoints[0].mp3FramesToDiscard = 0;
+ pSeekPoints[0].pcmFramesToDiscard = 0;
+ } else {
+ drmp3_uint64 pcmFramesBetweenSeekPoints;
+ drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1];
+ drmp3_uint64 runningPCMFrameCount = 0;
+ float runningPCMFrameCountFractionalPart = 0;
+ drmp3_uint64 nextTargetPCMFrame;
+ drmp3_uint32 iMP3Frame;
+ drmp3_uint32 iSeekPoint;
+
+ if (seekPointCount > totalMP3FrameCount-1) {
+ seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
+ }
+
+ pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
+
+ /*
+ Here is where we actually calculate the seek points. We need to start by moving the start of the stream. We then enumerate over each
+ MP3 frame.
+ */
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+
+ /*
+ We need to cache the byte positions of the previous MP3 frames. As a new MP3 frame is iterated, we cycle the byte positions in this
+ array. The value in the first item in this array is the byte position that will be reported in the next seek point.
+ */
+
+ /* We need to initialize the array of MP3 byte positions for the leading MP3 frames. */
+ for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
+
+ /* The byte position of the next frame will be the stream's cursor position, minus whatever is sitting in the buffer. */
+ DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
+ mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
+ mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
+
+ /* We need to get information about this frame so we can know how many samples it contained. */
+ pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, DRMP3_FALSE);
+ if (pcmFramesInCurrentMP3FrameIn == 0) {
+ return DRMP3_FALSE; /* This should never happen. */
+ }
+
+ drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
+ }
+
+ /*
+ At this point we will have extracted the byte positions of the leading MP3 frames. We can now start iterating over each seek point and
+ calculate them.
+ */
+ nextTargetPCMFrame = 0;
+ for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
+ nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
+
+ for (;;) {
+ if (nextTargetPCMFrame < runningPCMFrameCount) {
+ /* The next seek point is in the current MP3 frame. */
+ pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
+ pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
+ pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
+ pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
+ break;
+ } else {
+ size_t i;
+ drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
+
+ /*
+ The next seek point is not in the current MP3 frame, so continue on to the next one. The first thing to do is cycle the cached
+ MP3 frame info.
+ */
+ for (i = 0; i < drmp3_countof(mp3FrameInfo)-1; ++i) {
+ mp3FrameInfo[i] = mp3FrameInfo[i+1];
+ }
+
+ /* Cache previous MP3 frame info. */
+ mp3FrameInfo[drmp3_countof(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
+ mp3FrameInfo[drmp3_countof(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
+
+ /*
+ Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it
+ should only ever do it for the last seek point.
+ */
+ pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL, DRMP3_TRUE);
+ if (pcmFramesInCurrentMP3FrameIn == 0) {
+ pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
+ pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
+ pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
+ pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
+ break;
+ }
+
+ drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
+ }
+ }
+ }
+
+ /* Finally, we need to seek back to where we were. */
+ if (!drmp3_seek_to_start_of_stream(pMP3)) {
+ return DRMP3_FALSE;
+ }
+ if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
+ return DRMP3_FALSE;
+ }
+ }
+
+ *pSeekPointCount = seekPointCount;
+ return DRMP3_TRUE;
+}
+
+drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints)
+{
+ if (pMP3 == NULL) {
+ return DRMP3_FALSE;
+ }
+
+ if (seekPointCount == 0 || pSeekPoints == NULL) {
+ /* Unbinding. */
+ pMP3->seekPointCount = 0;
+ pMP3->pSeekPoints = NULL;
+ } else {
+ /* Binding. */
+ pMP3->seekPointCount = seekPointCount;
+ pMP3->pSeekPoints = pSeekPoints;
+ }
+
+ return DRMP3_TRUE;
+}
+
+
+float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3_uint64 totalFramesRead = 0;
+ drmp3_uint64 framesCapacity = 0;
+ float* pFrames = NULL;
+ float temp[4096];
+
+ DRMP3_ASSERT(pMP3 != NULL);
+
+ for (;;) {
+ drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels;
+ drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
+ if (framesJustRead == 0) {
+ break;
+ }
+
+ /* Reallocate the output buffer if there's not enough room. */
+ if (framesCapacity < totalFramesRead + framesJustRead) {
+ drmp3_uint64 oldFramesBufferSize;
+ drmp3_uint64 newFramesBufferSize;
+ drmp3_uint64 newFramesCap;
+ float* pNewFrames;
+
+ newFramesCap = framesCapacity * 2;
+ if (newFramesCap < totalFramesRead + framesJustRead) {
+ newFramesCap = totalFramesRead + framesJustRead;
+ }
+
+ oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
+ newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
+ if (newFramesBufferSize > DRMP3_SIZE_MAX) {
+ break;
+ }
+
+ pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
+ if (pNewFrames == NULL) {
+ drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
+ break;
+ }
+
+ pFrames = pNewFrames;
+ framesCapacity = newFramesCap;
+ }
+
+ 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;
+}
+
+drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
+{
+ drmp3_uint64 totalFramesRead = 0;
+ drmp3_uint64 framesCapacity = 0;
+ drmp3_int16* pFrames = NULL;
+ drmp3_int16 temp[4096];
+
+ DRMP3_ASSERT(pMP3 != NULL);
+
+ for (;;) {
+ drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels;
+ drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
+ if (framesJustRead == 0) {
+ break;
+ }
+
+ /* Reallocate the output buffer if there's not enough room. */
+ if (framesCapacity < totalFramesRead + framesJustRead) {
+ drmp3_uint64 newFramesBufferSize;
+ drmp3_uint64 oldFramesBufferSize;
+ drmp3_uint64 newFramesCap;
+ drmp3_int16* pNewFrames;
+
+ newFramesCap = framesCapacity * 2;
+ if (newFramesCap < totalFramesRead + framesJustRead) {
+ newFramesCap = totalFramesRead + framesJustRead;
+ }
+
+ oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16);
+ newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16);
+ if (newFramesBufferSize > DRMP3_SIZE_MAX) {
+ break;
+ }
+
+ pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
+ if (pNewFrames == NULL) {
+ drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
+ break;
+ }
+
+ pFrames = pNewFrames;
+ }
+
+ DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16)));
+ 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_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ drmp3 mp3;
+ if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
+}
+
+drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ drmp3 mp3;
+ if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pConfig, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
+}
+
+
+float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ drmp3 mp3;
+ if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
+}
+
+drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ drmp3 mp3;
+ if (!drmp3_init_memory(&mp3, pData, dataSize, pConfig, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
+}
+
+
+#ifndef DR_MP3_NO_STDIO
+float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ drmp3 mp3;
+ if (!drmp3_init_file(&mp3, filePath, pConfig, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
+}
+
+drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ drmp3 mp3;
+ if (!drmp3_init_file(&mp3, filePath, pConfig, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
+}
+#endif
+
+void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks != NULL) {
+ drmp3__free_from_callbacks(p, pAllocationCallbacks);
+ } else {
+ drmp3__free_default(p, NULL);
+ }
+}
+
+#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.5.0 - 2019-10-07
+ - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation
+ routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:
+ - drmp3_init()
+ - drmp3_init_file()
+ - drmp3_init_memory()
+ - drmp3_open_and_read_pcm_frames_f32()
+ - drmp3_open_and_read_pcm_frames_s16()
+ - drmp3_open_memory_and_read_pcm_frames_f32()
+ - drmp3_open_memory_and_read_pcm_frames_s16()
+ - drmp3_open_file_and_read_pcm_frames_f32()
+ - drmp3_open_file_and_read_pcm_frames_s16()
+ - API CHANGE: Renamed the following APIs:
+ - drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32()
+ - drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16()
+ - drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32()
+ - drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16()
+ - drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32()
+ - drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16()
+
+v0.4.7 - 2019-07-28
+ - Fix a compiler error.
+
+v0.4.6 - 2019-06-14
+ - Fix a compiler error.
+
+v0.4.5 - 2019-06-06
+ - Bring up to date with minimp3.
+
+v0.4.4 - 2019-05-06
+ - Fixes to the VC6 build.
+
+v0.4.3 - 2019-05-05
+ - Use the channel count and/or sample rate of the first MP3 frame instead of DR_MP3_DEFAULT_CHANNELS and
+ DR_MP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old behaviour, just set the relevant property to
+ DR_MP3_DEFAULT_CHANNELS or DR_MP3_DEFAULT_SAMPLE_RATE.
+ - Add s16 reading APIs
+ - drmp3_read_pcm_frames_s16
+ - drmp3_open_memory_and_read_pcm_frames_s16
+ - drmp3_open_and_read_pcm_frames_s16
+ - drmp3_open_file_and_read_pcm_frames_s16
+ - Add drmp3_get_mp3_and_pcm_frame_count() to the public header section.
+ - Add support for C89.
+ - Change license to choice of public domain or MIT-0.
+
+v0.4.2 - 2019-02-21
+ - Fix a warning.
+
+v0.4.1 - 2018-12-30
+ - Fix a warning.
+
+v0.4.0 - 2018-12-16
+ - API CHANGE: Rename some APIs:
+ - drmp3_read_f32 -> to drmp3_read_pcm_frames_f32
+ - drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame
+ - drmp3_open_and_decode_f32 -> drmp3_open_and_read_pcm_frames_f32
+ - drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_pcm_frames_f32
+ - drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_pcm_frames_f32
+ - Add drmp3_get_pcm_frame_count().
+ - Add drmp3_get_mp3_frame_count().
+ - Improve seeking performance.
+
+v0.3.2 - 2018-09-11
+ - Fix a couple of memory leaks.
+ - Bring up to date with minimp3.
+
+v0.3.1 - 2018-08-25
+ - Fix C++ build.
+
+v0.3.0 - 2018-08-25
+ - Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has
+ been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or
+ not the DR_MP3_FLOAT_OUTPUT option is set.
+
+v0.2.11 - 2018-08-08
+ - Fix a bug where the last part of a file is not read.
+
+v0.2.10 - 2018-08-07
+ - Improve 64-bit detection.
+
+v0.2.9 - 2018-08-05
+ - Fix C++ build on older versions of GCC.
+ - Bring up to date with minimp3.
+
+v0.2.8 - 2018-08-02
+ - Fix compilation errors with older versions of GCC.
+
+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 software is available as a choice of the following licenses. Choose
+whichever you prefer.
+
+===============================================================================
+ALTERNATIVE 1 - 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.
+
+For more information, please refer to <http://unlicense.org/>
+
+===============================================================================
+ALTERNATIVE 2 - MIT No Attribution
+===============================================================================
+Copyright 2018 David Reid
+
+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.
+
+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.
+*/
+
+/*
+ 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 <http://creativecommons.org/publicdomain/zero/1.0/>.
+*/
diff --git a/src/libs/decoders/dr_wav.h b/src/libs/decoders/dr_wav.h
new file mode 100644
index 000000000..9a7951105
--- /dev/null
+++ b/src/libs/decoders/dr_wav.h
@@ -0,0 +1,5334 @@
+/*
+WAV audio loader and writer. Choice of public domain or MIT-0. See license statements at the end of this file.
+dr_wav - v0.11.1 - 2019-10-07
+
+David Reid - mackron@gmail.com
+*/
+
+/*
+RELEASE NOTES - v0.11.0
+=======================
+Version 0.11.0 has breaking API changes.
+
+Improved Client-Defined Memory Allocation
+-----------------------------------------
+The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
+existing system of DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE are still in place and will be used by default when no custom
+allocation callbacks are specified.
+
+To use the new system, you pass in a pointer to a drwav_allocation_callbacks object to drwav_init() and family, like this:
+
+ void* my_malloc(size_t sz, void* pUserData)
+ {
+ return malloc(sz);
+ }
+ void* my_realloc(void* p, size_t sz, void* pUserData)
+ {
+ return realloc(p, sz);
+ }
+ void my_free(void* p, void* pUserData)
+ {
+ free(p);
+ }
+
+ ...
+
+ drwav_allocation_callbacks allocationCallbacks;
+ allocationCallbacks.pUserData = &myData;
+ allocationCallbacks.onMalloc = my_malloc;
+ allocationCallbacks.onRealloc = my_realloc;
+ allocationCallbacks.onFree = my_free;
+ drwav_init_file(&wav, "my_file.wav", &allocationCallbacks);
+
+The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
+
+Passing in null for the allocation callbacks object will cause dr_wav to use defaults which is the same as DRWAV_MALLOC,
+DRWAV_REALLOC and DRWAV_FREE and the equivalent of how it worked in previous versions.
+
+Every API that opens a drwav object now takes this extra parameter. These include the following:
+
+ drwav_init()
+ drwav_init_ex()
+ drwav_init_file()
+ drwav_init_file_ex()
+ drwav_init_file_w()
+ drwav_init_file_w_ex()
+ drwav_init_memory()
+ drwav_init_memory_ex()
+ drwav_init_write()
+ drwav_init_write_sequential()
+ drwav_init_write_sequential_pcm_frames()
+ drwav_init_file_write()
+ drwav_init_file_write_sequential()
+ drwav_init_file_write_sequential_pcm_frames()
+ drwav_init_file_write_w()
+ drwav_init_file_write_sequential_w()
+ drwav_init_file_write_sequential_pcm_frames_w()
+ drwav_init_memory_write()
+ drwav_init_memory_write_sequential()
+ drwav_init_memory_write_sequential_pcm_frames()
+ drwav_open_and_read_pcm_frames_s16()
+ drwav_open_and_read_pcm_frames_f32()
+ drwav_open_and_read_pcm_frames_s32()
+ drwav_open_file_and_read_pcm_frames_s16()
+ drwav_open_file_and_read_pcm_frames_f32()
+ drwav_open_file_and_read_pcm_frames_s32()
+ drwav_open_file_and_read_pcm_frames_s16_w()
+ drwav_open_file_and_read_pcm_frames_f32_w()
+ drwav_open_file_and_read_pcm_frames_s32_w()
+ drwav_open_memory_and_read_pcm_frames_s16()
+ drwav_open_memory_and_read_pcm_frames_f32()
+ drwav_open_memory_and_read_pcm_frames_s32()
+
+Endian Improvements
+-------------------
+Previously, the following APIs returned little-endian audio data. These now return native-endian data. This improves compatibility
+on big-endian architectures.
+
+ drwav_read_pcm_frames()
+ drwav_read_pcm_frames_s16()
+ drwav_read_pcm_frames_s32()
+ drwav_read_pcm_frames_f32()
+ drwav_open_and_read_pcm_frames_s16()
+ drwav_open_and_read_pcm_frames_s32()
+ drwav_open_and_read_pcm_frames_f32()
+ drwav_open_file_and_read_pcm_frames_s16()
+ drwav_open_file_and_read_pcm_frames_s32()
+ drwav_open_file_and_read_pcm_frames_f32()
+ drwav_open_file_and_read_pcm_frames_s16_w()
+ drwav_open_file_and_read_pcm_frames_s32_w()
+ drwav_open_file_and_read_pcm_frames_f32_w()
+ drwav_open_memory_and_read_pcm_frames_s16()
+ drwav_open_memory_and_read_pcm_frames_s32()
+ drwav_open_memory_and_read_pcm_frames_f32()
+
+APIs have been added to give you explicit control over whether or not audio data is read or written in big- or little-endian byte
+order:
+
+ drwav_read_pcm_frames_le()
+ drwav_read_pcm_frames_be()
+ drwav_read_pcm_frames_s16le()
+ drwav_read_pcm_frames_s16be()
+ drwav_read_pcm_frames_f32le()
+ drwav_read_pcm_frames_f32be()
+ drwav_read_pcm_frames_s32le()
+ drwav_read_pcm_frames_s32be()
+ drwav_write_pcm_frames_le()
+ drwav_write_pcm_frames_be()
+
+Removed APIs
+------------
+The following APIs were deprecated in version 0.10.0 and have now been removed:
+
+ drwav_open()
+ drwav_open_ex()
+ drwav_open_write()
+ drwav_open_write_sequential()
+ drwav_open_file()
+ drwav_open_file_ex()
+ drwav_open_file_write()
+ drwav_open_file_write_sequential()
+ drwav_open_memory()
+ drwav_open_memory_ex()
+ drwav_open_memory_write()
+ drwav_open_memory_write_sequential()
+ drwav_close()
+
+
+
+RELEASE NOTES - v0.10.0
+=======================
+Version 0.10.0 has breaking API changes. There are no significant bug fixes in this release, so if you are affected you do
+not need to upgrade.
+
+Removed APIs
+------------
+The following APIs were deprecated in version 0.9.0 and have been completely removed in version 0.10.0:
+
+ drwav_read()
+ drwav_read_s16()
+ drwav_read_f32()
+ drwav_read_s32()
+ drwav_seek_to_sample()
+ drwav_write()
+ drwav_open_and_read_s16()
+ drwav_open_and_read_f32()
+ drwav_open_and_read_s32()
+ drwav_open_file_and_read_s16()
+ drwav_open_file_and_read_f32()
+ drwav_open_file_and_read_s32()
+ drwav_open_memory_and_read_s16()
+ drwav_open_memory_and_read_f32()
+ drwav_open_memory_and_read_s32()
+ drwav::totalSampleCount
+
+See release notes for version 0.9.0 at the bottom of this file for replacement APIs.
+
+Deprecated APIs
+---------------
+The following APIs have been deprecated. There is a confusing and completely arbitrary difference between drwav_init*() and
+drwav_open*(), where drwav_init*() initializes a pre-allocated drwav object, whereas drwav_open*() will first allocated a
+drwav object on the heap and then initialize it. drwav_open*() has been deprecated which means you must now use a pre-
+allocated drwav object with drwav_init*(). If you need the previous functionality, you can just do a malloc() followed by
+a called to one of the drwav_init*() APIs.
+
+ drwav_open()
+ drwav_open_ex()
+ drwav_open_write()
+ drwav_open_write_sequential()
+ drwav_open_file()
+ drwav_open_file_ex()
+ drwav_open_file_write()
+ drwav_open_file_write_sequential()
+ drwav_open_memory()
+ drwav_open_memory_ex()
+ drwav_open_memory_write()
+ drwav_open_memory_write_sequential()
+ drwav_close()
+
+These APIs will be removed completely in a future version. The rationale for this change is to remove confusion between the
+two different ways to initialize a drwav object.
+*/
+
+/*
+USAGE
+=====
+This is a single-file library. To use it, do something like the following in one .c file.
+ #define DR_WAV_IMPLEMENTATION
+ #include "dr_wav.h"
+
+You can then #include this file in other parts of the program as you would with any other header file. Do something
+like the following to read audio data:
+
+ drwav wav;
+ if (!drwav_init_file(&wav, "my_song.wav")) {
+ // Error opening WAV file.
+ }
+
+ drwav_int32* pDecodedInterleavedPCMFrames = malloc(wav.totalPCMFrameCount * wav.channels * sizeof(drwav_int32));
+ size_t numberOfSamplesActuallyDecoded = drwav_read_pcm_frames_s32(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
+
+ ...
+
+ drwav_uninit(&wav);
+
+If you just want to quickly open and read the audio data in a single operation you can do something like this:
+
+ unsigned int channels;
+ unsigned int sampleRate;
+ drwav_uint64 totalPCMFrameCount;
+ float* pSampleData = drwav_open_file_and_read_pcm_frames_f32("my_song.wav", &channels, &sampleRate, &totalPCMFrameCount);
+ if (pSampleData == NULL) {
+ // Error opening and reading WAV file.
+ }
+
+ ...
+
+ drwav_free(pSampleData);
+
+The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in
+this case), but you can still output the audio data in its internal format (see notes below for supported formats):
+
+ size_t framesRead = drwav_read_pcm_frames(&wav, wav.totalPCMFrameCount, pDecodedInterleavedPCMFrames);
+
+You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for
+a particular data format:
+
+ size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer);
+
+
+dr_wav can also be used to output WAV files. This does not currently support compressed formats. To use this, look at
+drwav_init_write(), drwav_init_file_write(), etc. Use drwav_write_pcm_frames() to write samples, or drwav_write_raw()
+to write raw data in the "data" chunk.
+
+ drwav_data_format format;
+ format.container = drwav_container_riff; // <-- drwav_container_riff = normal WAV files, drwav_container_w64 = Sony Wave64.
+ format.format = DR_WAVE_FORMAT_PCM; // <-- Any of the DR_WAVE_FORMAT_* codes.
+ format.channels = 2;
+ format.sampleRate = 44100;
+ format.bitsPerSample = 16;
+ drwav_init_file_write(&wav, "data/recording.wav", &format);
+
+ ...
+
+ drwav_uint64 framesWritten = drwav_write_pcm_frames(pWav, frameCount, pSamples);
+
+
+dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work
+without any manual intervention.
+
+
+OPTIONS
+=======
+#define these options before including this file.
+
+#define DR_WAV_NO_CONVERSION_API
+ Disables conversion APIs such as drwav_read_pcm_frames_f32() and drwav_s16_to_f32().
+
+#define DR_WAV_NO_STDIO
+ Disables APIs that initialize a decoder from a file such as drwav_init_file(), drwav_init_file_write(), etc.
+
+
+
+QUICK NOTES
+===========
+- Samples are always interleaved.
+- The default read function does not do any data conversion. Use drwav_read_pcm_frames_f32(), drwav_read_pcm_frames_s32()
+ and drwav_read_pcm_frames_s16() to read and convert audio data to 32-bit floating point, signed 32-bit integer and
+ signed 16-bit integer samples respectively. Tested and supported internal formats include the following:
+ - Unsigned 8-bit PCM
+ - Signed 12-bit PCM
+ - Signed 16-bit PCM
+ - Signed 24-bit PCM
+ - Signed 32-bit PCM
+ - IEEE 32-bit floating point
+ - IEEE 64-bit floating point
+ - A-law and u-law
+ - Microsoft ADPCM
+ - IMA ADPCM (DVI, format code 0x11)
+- dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format.
+*/
+
+#ifndef dr_wav_h
+#define dr_wav_h
+
+#include <stddef.h>
+
+#if defined(_MSC_VER) && _MSC_VER < 1600
+typedef signed char drwav_int8;
+typedef unsigned char drwav_uint8;
+typedef signed short drwav_int16;
+typedef unsigned short drwav_uint16;
+typedef signed int drwav_int32;
+typedef unsigned int drwav_uint32;
+typedef signed __int64 drwav_int64;
+typedef unsigned __int64 drwav_uint64;
+#else
+#include <stdint.h>
+typedef int8_t drwav_int8;
+typedef uint8_t drwav_uint8;
+typedef int16_t drwav_int16;
+typedef uint16_t drwav_uint16;
+typedef int32_t drwav_int32;
+typedef uint32_t drwav_uint32;
+typedef int64_t drwav_int64;
+typedef uint64_t drwav_uint64;
+#endif
+typedef drwav_uint8 drwav_bool8;
+typedef drwav_uint32 drwav_bool32;
+#define DRWAV_TRUE 1
+#define DRWAV_FALSE 0
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef drwav_int32 drwav_result;
+#define DRWAV_SUCCESS 0
+#define DRWAV_ERROR -1
+#define DRWAV_INVALID_ARGS -2
+#define DRWAV_INVALID_OPERATION -3
+#define DRWAV_INVALID_FILE -100
+#define DRWAV_EOF -101
+
+/* Common data formats. */
+#define DR_WAVE_FORMAT_PCM 0x1
+#define DR_WAVE_FORMAT_ADPCM 0x2
+#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3
+#define DR_WAVE_FORMAT_ALAW 0x6
+#define DR_WAVE_FORMAT_MULAW 0x7
+#define DR_WAVE_FORMAT_DVI_ADPCM 0x11
+#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
+
+/* Constants. */
+#ifndef DRWAV_MAX_SMPL_LOOPS
+#define DRWAV_MAX_SMPL_LOOPS 1
+#endif
+
+/* Flags to pass into drwav_init_ex(), etc. */
+#define DRWAV_SEQUENTIAL 0x00000001
+
+typedef enum
+{
+ drwav_seek_origin_start,
+ drwav_seek_origin_current
+} drwav_seek_origin;
+
+typedef enum
+{
+ drwav_container_riff,
+ drwav_container_w64
+} drwav_container;
+
+typedef struct
+{
+ union
+ {
+ drwav_uint8 fourcc[4];
+ drwav_uint8 guid[16];
+ } id;
+
+ /* The size in bytes of the chunk. */
+ drwav_uint64 sizeInBytes;
+
+ /*
+ RIFF = 2 byte alignment.
+ W64 = 8 byte alignment.
+ */
+ unsigned int paddingSize;
+} drwav_chunk_header;
+
+/*
+Callback for when data is read. Return value is the number of bytes actually read.
+
+pUserData [in] The user data that was passed to drwav_init() 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 (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
+
+/*
+Callback for when data is written. Returns value is the number of bytes actually written.
+
+pUserData [in] The user data that was passed to drwav_init_write() and family.
+pData [out] A pointer to the data to write.
+bytesToWrite [in] The number of bytes to write.
+
+Returns the number of bytes actually written.
+
+If the return value differs from bytesToWrite, it indicates an error.
+*/
+typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
+
+/*
+Callback for when data needs to be seeked.
+
+pUserData [in] The user data that was passed to drwav_init() 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 drwav_seek_origin_start or drwav_seek_origin_current.
+*/
+typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin);
+
+/*
+Callback for when drwav_init_ex() finds a chunk.
+
+pChunkUserData [in] The user data that was passed to the pChunkUserData parameter of drwav_init_ex() and family.
+onRead [in] A pointer to the function to call when reading.
+onSeek [in] A pointer to the function to call when seeking.
+pReadSeekUserData [in] The user data that was passed to the pReadSeekUserData parameter of drwav_init_ex() and family.
+pChunkHeader [in] A pointer to an object containing basic header information about the chunk. Use this to identify the chunk.
+
+Returns the number of bytes read + seeked.
+
+To read data from the chunk, call onRead(), passing in pReadSeekUserData as the first parameter. Do the same
+for seeking with onSeek(). The return value must be the total number of bytes you have read _plus_ seeked.
+
+You must not attempt to read beyond the boundary of the chunk.
+*/
+typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader);
+
+typedef struct
+{
+ void* pUserData;
+ void* (* onMalloc)(size_t sz, void* pUserData);
+ void* (* onRealloc)(void* p, size_t sz, void* pUserData);
+ void (* onFree)(void* p, void* pUserData);
+} drwav_allocation_callbacks;
+
+/* Structure for internal use. Only used for loaders opened with drwav_init_memory(). */
+typedef struct
+{
+ const drwav_uint8* data;
+ size_t dataSize;
+ size_t currentReadPos;
+} drwav__memory_stream;
+
+/* Structure for internal use. Only used for writers opened with drwav_init_memory_write(). */
+typedef struct
+{
+ void** ppData;
+ size_t* pDataSize;
+ size_t dataSize;
+ size_t dataCapacity;
+ size_t currentWritePos;
+} drwav__memory_stream_write;
+
+typedef struct
+{
+ drwav_container container; /* RIFF, W64. */
+ drwav_uint32 format; /* DR_WAVE_FORMAT_* */
+ drwav_uint32 channels;
+ drwav_uint32 sampleRate;
+ drwav_uint32 bitsPerSample;
+} drwav_data_format;
+
+typedef struct
+{
+ /*
+ The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications
+ that require support for data formats not natively supported by dr_wav.
+ */
+ drwav_uint16 formatTag;
+
+ /* The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. */
+ drwav_uint16 channels;
+
+ /* The sample rate. Usually set to something like 44100. */
+ drwav_uint32 sampleRate;
+
+ /* Average bytes per second. You probably don't need this, but it's left here for informational purposes. */
+ drwav_uint32 avgBytesPerSec;
+
+ /* Block align. This is equal to the number of channels * bytes per sample. */
+ drwav_uint16 blockAlign;
+
+ /* Bits per sample. */
+ drwav_uint16 bitsPerSample;
+
+ /* The size of the extended data. Only used internally for validation, but left here for informational purposes. */
+ drwav_uint16 extendedSize;
+
+ /*
+ The number of valid bits per sample. When <formatTag> is equal to WAVE_FORMAT_EXTENSIBLE, <bitsPerSample>
+ is always rounded up to the nearest multiple of 8. This variable contains information about exactly how
+ many bits a valid per sample. Mainly used for informational purposes.
+ */
+ drwav_uint16 validBitsPerSample;
+
+ /* The channel mask. Not used at the moment. */
+ drwav_uint32 channelMask;
+
+ /* The sub-format, exactly as specified by the wave file. */
+ drwav_uint8 subFormat[16];
+} drwav_fmt;
+
+typedef struct
+{
+ drwav_uint32 cuePointId;
+ drwav_uint32 type;
+ drwav_uint32 start;
+ drwav_uint32 end;
+ drwav_uint32 fraction;
+ drwav_uint32 playCount;
+} drwav_smpl_loop;
+
+ typedef struct
+{
+ drwav_uint32 manufacturer;
+ drwav_uint32 product;
+ drwav_uint32 samplePeriod;
+ drwav_uint32 midiUnityNotes;
+ drwav_uint32 midiPitchFraction;
+ drwav_uint32 smpteFormat;
+ drwav_uint32 smpteOffset;
+ drwav_uint32 numSampleLoops;
+ drwav_uint32 samplerData;
+ drwav_smpl_loop loops[DRWAV_MAX_SMPL_LOOPS];
+} drwav_smpl;
+
+typedef struct
+{
+ /* A pointer to the function to call when more data is needed. */
+ drwav_read_proc onRead;
+
+ /* A pointer to the function to call when data needs to be written. Only used when the drwav object is opened in write mode. */
+ drwav_write_proc onWrite;
+
+ /* A pointer to the function to call when the wav file needs to be seeked. */
+ drwav_seek_proc onSeek;
+
+ /* The user data to pass to callbacks. */
+ void* pUserData;
+
+ /* Allocation callbacks. */
+ drwav_allocation_callbacks allocationCallbacks;
+
+
+ /* Whether or not the WAV file is formatted as a standard RIFF file or W64. */
+ drwav_container container;
+
+
+ /* Structure containing format information exactly as specified by the wav file. */
+ drwav_fmt fmt;
+
+ /* The sample rate. Will be set to something like 44100. */
+ drwav_uint32 sampleRate;
+
+ /* The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. */
+ drwav_uint16 channels;
+
+ /* The bits per sample. Will be set to something like 16, 24, etc. */
+ drwav_uint16 bitsPerSample;
+
+ /* Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). */
+ drwav_uint16 translatedFormatTag;
+
+ /* The total number of PCM frames making up the audio data. */
+ drwav_uint64 totalPCMFrameCount;
+
+
+ /* The size in bytes of the data chunk. */
+ drwav_uint64 dataChunkDataSize;
+
+ /* The position in the stream of the first byte of the data chunk. This is used for seeking. */
+ drwav_uint64 dataChunkDataPos;
+
+ /* The number of bytes remaining in the data chunk. */
+ drwav_uint64 bytesRemaining;
+
+
+ /*
+ Only used in sequential write mode. Keeps track of the desired size of the "data" chunk at the point of initialization time. Always
+ set to 0 for non-sequential writes and when the drwav object is opened in read mode. Used for validation.
+ */
+ drwav_uint64 dataChunkDataSizeTargetWrite;
+
+ /* Keeps track of whether or not the wav writer was initialized in sequential mode. */
+ drwav_bool32 isSequentialWrite;
+
+
+ /* smpl chunk. */
+ drwav_smpl smpl;
+
+
+ /* A hack to avoid a DRWAV_MALLOC() when opening a decoder with drwav_init_memory(). */
+ drwav__memory_stream memoryStream;
+ drwav__memory_stream_write memoryStreamWrite;
+
+ /* Generic data for compressed formats. This data is shared across all block-compressed formats. */
+ struct
+ {
+ drwav_uint64 iCurrentPCMFrame; /* The index of the next PCM frame that will be read by drwav_read_*(). This is used with "totalPCMFrameCount" to ensure we don't read excess samples at the end of the last block. */
+ } compressed;
+
+ /* Microsoft ADPCM specific data. */
+ struct
+ {
+ drwav_uint32 bytesRemainingInBlock;
+ drwav_uint16 predictor[2];
+ drwav_int32 delta[2];
+ drwav_int32 cachedFrames[4]; /* Samples are stored in this cache during decoding. */
+ drwav_uint32 cachedFrameCount;
+ drwav_int32 prevFrames[2][2]; /* The previous 2 samples for each channel (2 channels at most). */
+ } msadpcm;
+
+ /* IMA ADPCM specific data. */
+ struct
+ {
+ drwav_uint32 bytesRemainingInBlock;
+ drwav_int32 predictor[2];
+ drwav_int32 stepIndex[2];
+ drwav_int32 cachedFrames[16]; /* Samples are stored in this cache during decoding. */
+ drwav_uint32 cachedFrameCount;
+ } ima;
+} drwav;
+
+
+/*
+Initializes a pre-allocated drwav object for reading.
+
+pWav [out] A pointer to the drwav object being initialized.
+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.
+onChunk [in, optional] The function to call when a chunk is enumerated at initialized time.
+pUserData, pReadSeekUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
+pChunkUserData [in, optional] A pointer to application defined data that will be passed to onChunk.
+flags [in, optional] A set of flags for controlling how things are loaded.
+
+Returns true if successful; false otherwise.
+
+Close the loader with drwav_uninit().
+
+This is the lowest level function for initializing a WAV file. You can also use drwav_init_file() and drwav_init_memory()
+to open the stream from a file or from a block of memory respectively.
+
+Possible values for flags:
+ DRWAV_SEQUENTIAL: Never perform a backwards seek while loading. This disables the chunk callback and will cause this function
+ to return as soon as the data chunk is found. Any chunks after the data chunk will be ignored.
+
+drwav_init() is equivalent to "drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0);".
+
+The onChunk callback is not called for the WAVE or FMT chunks. The contents of the FMT chunk can be read from pWav->fmt
+after the function returns.
+
+See also: drwav_init_file(), drwav_init_memory(), drwav_uninit()
+*/
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
+
+/*
+Initializes a pre-allocated drwav object for writing.
+
+onWrite [in] The function to call when data needs to be written.
+onSeek [in] The function to call when the write position needs to move.
+pUserData [in, optional] A pointer to application defined data that will be passed to onWrite and onSeek.
+
+Returns true if successful; false otherwise.
+
+Close the writer with drwav_uninit().
+
+This is the lowest level function for initializing a WAV file. You can also use drwav_init_file_write() and drwav_init_memory_write()
+to open the stream from a file or from a block of memory respectively.
+
+If the total sample count is known, you can use drwav_init_write_sequential(). This avoids the need for dr_wav to perform
+a post-processing step for storing the total sample count and the size of the data chunk which requires a backwards seek.
+
+See also: drwav_init_file_write(), drwav_init_memory_write(), drwav_uninit()
+*/
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks);
+
+/*
+Utility function to determine the target size of the entire data to be written (including all headers and chunks).
+
+Returns the target size in bytes.
+
+Useful if the application needs to know the size to allocate.
+
+Only writing to the RIFF chunk and one data chunk is currently supported.
+
+See also: drwav_init_write(), drwav_init_file_write(), drwav_init_memory_write()
+*/
+drwav_uint64 drwav_target_write_size_bytes(drwav_data_format const *format, drwav_uint64 totalSampleCount);
+
+/*
+Uninitializes the given drwav object.
+
+Use this only for objects initialized with drwav_init*() functions (drwav_init(), drwav_init_ex(), drwav_init_write(), drwav_init_write_sequential()).
+*/
+drwav_result drwav_uninit(drwav* pWav);
+
+
+/*
+Reads raw audio data.
+
+This is the lowest level function for reading audio data. It simply reads the given number of
+bytes of the raw internal sample data.
+
+Consider using drwav_read_pcm_frames_s16(), drwav_read_pcm_frames_s32() or drwav_read_pcm_frames_f32() for
+reading sample data in a consistent format.
+
+Returns the number of bytes actually read.
+*/
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut);
+
+/*
+Reads up to the specified number of PCM frames from the WAV file.
+
+The output data will be in the file's internal format, converted to native-endian byte order. Use
+drwav_read_pcm_frames_s16/f32/s32() to read data in a specific format.
+
+If the return value is less than <framesToRead> it means the end of the file has been reached or
+you have requested more PCM frames than can possibly fit in the output buffer.
+
+This function will only work when sample data is of a fixed size and uncompressed. If you are
+using a compressed format consider using drwav_read_raw() or drwav_read_pcm_frames_s16/s32/f32().
+*/
+drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut);
+
+/*
+Seeks to the given PCM frame.
+
+Returns true if successful; false otherwise.
+*/
+drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex);
+
+
+/*
+Writes raw audio data.
+
+Returns the number of bytes actually written. If this differs from bytesToWrite, it indicates an error.
+*/
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData);
+
+/*
+Writes PCM frames.
+
+Returns the number of PCM frames written.
+
+Input samples need to be in native-endian byte order. On big-endian architectures the input data will be converted to
+little-endian. Use drwav_write_raw() to write raw audio data without performing any conversion.
+*/
+drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
+drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
+drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData);
+
+
+/* Conversion Utilities */
+#ifndef DR_WAV_NO_CONVERSION_API
+
+/*
+Reads a chunk of audio data and converts it to signed 16-bit PCM samples.
+
+Returns the number of PCM frames actually read.
+
+If the return value is less than <framesToRead> it means the end of the file has been reached.
+*/
+drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut);
+
+/* Low-level function for converting unsigned 8-bit PCM samples to signed 16-bit PCM samples. */
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting signed 24-bit PCM samples to signed 16-bit PCM samples. */
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting signed 32-bit PCM samples to signed 16-bit PCM samples. */
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+/* Low-level function for converting IEEE 32-bit floating point samples to signed 16-bit PCM samples. */
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount);
+
+/* Low-level function for converting IEEE 64-bit floating point samples to signed 16-bit PCM samples. */
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount);
+
+/* Low-level function for converting A-law samples to signed 16-bit PCM samples. */
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting u-law samples to signed 16-bit PCM samples. */
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+/*
+Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples.
+
+Returns the number of PCM frames actually read.
+
+If the return value is less than <framesToRead> it means the end of the file has been reached.
+*/
+drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut);
+
+/* Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. */
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. */
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+/* Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. */
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. */
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount);
+
+/* Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. */
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
+
+/* Low-level function for converting A-law samples to IEEE 32-bit floating point samples. */
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting u-law samples to IEEE 32-bit floating point samples. */
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+
+/*
+Reads a chunk of audio data and converts it to signed 32-bit PCM samples.
+
+Returns the number of PCM frames actually read.
+
+If the return value is less than <framesToRead> it means the end of the file has been reached.
+*/
+drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut);
+
+/* Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. */
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. */
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount);
+
+/* Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. */
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. */
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount);
+
+/* Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. */
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount);
+
+/* Low-level function for converting A-law samples to signed 32-bit PCM samples. */
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+/* Low-level function for converting u-law samples to signed 32-bit PCM samples. */
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount);
+
+#endif /* DR_WAV_NO_CONVERSION_API */
+
+
+/* High-Level Convenience Helpers */
+
+#ifndef DR_WAV_NO_STDIO
+/*
+Helper for initializing a wave file for reading using stdio.
+
+This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
+
+/*
+Helper for initializing a wave file for writing using stdio.
+
+This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav
+objects because the operating system may restrict the number of file handles an application can have open at
+any given time.
+*/
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
+#endif /* DR_WAV_NO_STDIO */
+
+/*
+Helper for initializing a loader from a pre-allocated memory buffer.
+
+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 drwav object.
+
+The buffer should contain the contents of the entire wave file, not just the sample data.
+*/
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks);
+
+/*
+Helper for initializing a writer which outputs data to a memory buffer.
+
+dr_wav will manage the memory allocations, however it is up to the caller to free the data with drwav_free().
+
+The buffer will remain allocated even after drwav_uninit() is called. Indeed, the buffer should not be
+considered valid until after drwav_uninit() has been called anyway.
+*/
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks);
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+/*
+Opens and reads an entire wav file in a single operation.
+
+The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
+*/
+drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+#ifndef DR_WAV_NO_STDIO
+/*
+Opens and decodes an entire wav file in a single operation.
+
+The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
+*/
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+#endif
+/*
+Opens and decodes an entire wav file from a block of memory in a single operation.
+
+The return value is a heap-allocated buffer containing the audio data. Use drwav_free() to free the buffer.
+*/
+drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks);
+#endif
+
+/* Frees data that was allocated internally by dr_wav. */
+void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* dr_wav_h */
+
+
+/************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************
+
+ IMPLEMENTATION
+
+ ************************************************************************************************************************************************************
+ ************************************************************************************************************************************************************/
+#ifdef DR_WAV_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h> /* For memcpy(), memset() */
+#include <limits.h> /* For INT_MAX */
+
+#ifndef DR_WAV_NO_STDIO
+#include <stdio.h>
+#include <wchar.h>
+#endif
+
+/* Standard library stuff. */
+#ifndef DRWAV_ASSERT
+#include <assert.h>
+#define DRWAV_ASSERT(expression) assert(expression)
+#endif
+#ifndef DRWAV_MALLOC
+#define DRWAV_MALLOC(sz) malloc((sz))
+#endif
+#ifndef DRWAV_REALLOC
+#define DRWAV_REALLOC(p, sz) realloc((p), (sz))
+#endif
+#ifndef DRWAV_FREE
+#define DRWAV_FREE(p) free((p))
+#endif
+#ifndef DRWAV_COPY_MEMORY
+#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
+#endif
+#ifndef DRWAV_ZERO_MEMORY
+#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
+#endif
+
+#define drwav_countof(x) (sizeof(x) / sizeof(x[0]))
+#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
+#define drwav_min(a, b) (((a) < (b)) ? (a) : (b))
+#define drwav_max(a, b) (((a) > (b)) ? (a) : (b))
+#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x))))
+
+#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 /* 64 for AVX-512 in the future. */
+
+/* CPU architecture. */
+#if defined(__x86_64__) || defined(_M_X64)
+ #define DRWAV_X64
+#elif defined(__i386) || defined(_M_IX86)
+ #define DRWAV_X86
+#elif defined(__arm__) || defined(_M_ARM)
+ #define DRWAV_ARM
+#endif
+
+#ifdef _MSC_VER
+ #define DRWAV_INLINE __forceinline
+#elif defined(__GNUC__)
+ /*
+ I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
+ the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
+ case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
+ command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
+ I am using "__inline__" only when we're compiling in strict ANSI mode.
+ */
+ #if defined(__STRICT_ANSI__)
+ #define DRWAV_INLINE __inline__ __attribute__((always_inline))
+ #else
+ #define DRWAV_INLINE inline __attribute__((always_inline))
+ #endif
+#else
+ #define DRWAV_INLINE
+#endif
+
+#if defined(SIZE_MAX)
+ #define DRWAV_SIZE_MAX SIZE_MAX
+#else
+ #if defined(_WIN64) || defined(_LP64) || defined(__LP64__)
+ #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF)
+ #else
+ #define DRWAV_SIZE_MAX 0xFFFFFFFF
+ #endif
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER >= 1300
+ #define DRWAV_HAS_BYTESWAP16_INTRINSIC
+ #define DRWAV_HAS_BYTESWAP32_INTRINSIC
+ #define DRWAV_HAS_BYTESWAP64_INTRINSIC
+#elif defined(__clang__)
+ #if defined(__has_builtin)
+ #if __has_builtin(__builtin_bswap16)
+ #define DRWAV_HAS_BYTESWAP16_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap32)
+ #define DRWAV_HAS_BYTESWAP32_INTRINSIC
+ #endif
+ #if __has_builtin(__builtin_bswap64)
+ #define DRWAV_HAS_BYTESWAP64_INTRINSIC
+ #endif
+ #endif
+#elif defined(__GNUC__)
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+ #define DRWAV_HAS_BYTESWAP32_INTRINSIC
+ #define DRWAV_HAS_BYTESWAP64_INTRINSIC
+ #endif
+ #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
+ #define DRWAV_HAS_BYTESWAP16_INTRINSIC
+ #endif
+#endif
+
+static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; /* 66666972-912E-11CF-A5D6-28DB04C10000 */
+static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 65766177-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_JUNK[16] = {0x6A,0x75,0x6E,0x6B, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 6B6E756A-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 74636166-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 61746164-ACF3-11D3-8CD1-00C04F8EDB8A */
+static const drwav_uint8 drwavGUID_W64_SMPL[16] = {0x73,0x6D,0x70,0x6C, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; /* 6C706D73-ACF3-11D3-8CD1-00C04F8EDB8A */
+
+static DRWAV_INLINE drwav_bool32 drwav__guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16])
+{
+ const drwav_uint32* a32 = (const drwav_uint32*)a;
+ const drwav_uint32* b32 = (const drwav_uint32*)b;
+
+ return
+ a32[0] == b32[0] &&
+ a32[1] == b32[1] &&
+ a32[2] == b32[2] &&
+ a32[3] == b32[3];
+}
+
+static DRWAV_INLINE drwav_bool32 drwav__fourcc_equal(const unsigned char* a, const char* b)
+{
+ return
+ a[0] == b[0] &&
+ a[1] == b[1] &&
+ a[2] == b[2] &&
+ a[3] == b[3];
+}
+
+
+
+static DRWAV_INLINE int drwav__is_little_endian()
+{
+#if defined(DRWAV_X86) || defined(DRWAV_X64)
+ return DRWAV_TRUE;
+#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
+ return DRWAV_TRUE;
+#else
+ int n = 1;
+ return (*(char*)&n) == 1;
+#endif
+}
+
+static DRWAV_INLINE unsigned short drwav__bytes_to_u16(const unsigned char* data)
+{
+ return (data[0] << 0) | (data[1] << 8);
+}
+
+static DRWAV_INLINE short drwav__bytes_to_s16(const unsigned char* data)
+{
+ return (short)drwav__bytes_to_u16(data);
+}
+
+static DRWAV_INLINE unsigned int drwav__bytes_to_u32(const unsigned char* data)
+{
+ return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24);
+}
+
+static DRWAV_INLINE drwav_uint64 drwav__bytes_to_u64(const unsigned char* data)
+{
+ return
+ ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) |
+ ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56);
+}
+
+static DRWAV_INLINE void drwav__bytes_to_guid(const unsigned char* data, drwav_uint8* guid)
+{
+ int i;
+ for (i = 0; i < 16; ++i) {
+ guid[i] = data[i];
+ }
+}
+
+
+static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n)
+{
+#ifdef DRWAV_HAS_BYTESWAP16_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 DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n)
+{
+#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC
+ #if defined(_MSC_VER)
+ return _byteswap_ulong(n);
+ #elif defined(__GNUC__) || defined(__clang__)
+ #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) /* <-- 64-bit inline assembly has not been tested, so disabling for now. */
+ /* Inline assembly optimized implementation for ARM. In my testing, GCC does not generate optimized code with __builtin_bswap32(). */
+ drwav_uint32 r;
+ __asm__ __volatile__ (
+ #if defined(DRWAV_64BIT)
+ "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) /* <-- This is untested. If someone in the community could test this, that would be appreciated! */
+ #else
+ "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
+ #endif
+ );
+ return r;
+ #else
+ return __builtin_bswap32(n);
+ #endif
+ #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 DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n)
+{
+#ifdef DRWAV_HAS_BYTESWAP64_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 & (drwav_uint64)0xFF00000000000000) >> 56) |
+ ((n & (drwav_uint64)0x00FF000000000000) >> 40) |
+ ((n & (drwav_uint64)0x0000FF0000000000) >> 24) |
+ ((n & (drwav_uint64)0x000000FF00000000) >> 8) |
+ ((n & (drwav_uint64)0x00000000FF000000) << 8) |
+ ((n & (drwav_uint64)0x0000000000FF0000) << 24) |
+ ((n & (drwav_uint64)0x000000000000FF00) << 40) |
+ ((n & (drwav_uint64)0x00000000000000FF) << 56);
+#endif
+}
+
+
+static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n)
+{
+ return (drwav_int16)drwav__bswap16((drwav_uint16)n);
+}
+
+static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount)
+{
+ drwav_uint64 iSample;
+ for (iSample = 0; iSample < sampleCount; iSample += 1) {
+ pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]);
+ }
+}
+
+
+static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p)
+{
+ drwav_uint8 t;
+ t = p[0];
+ p[0] = p[2];
+ p[2] = t;
+}
+
+static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount)
+{
+ drwav_uint64 iSample;
+ for (iSample = 0; iSample < sampleCount; iSample += 1) {
+ drwav_uint8* pSample = pSamples + (iSample*3);
+ drwav__bswap_s24(pSample);
+ }
+}
+
+
+static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n)
+{
+ return (drwav_int32)drwav__bswap32((drwav_uint32)n);
+}
+
+static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount)
+{
+ drwav_uint64 iSample;
+ for (iSample = 0; iSample < sampleCount; iSample += 1) {
+ pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]);
+ }
+}
+
+
+static DRWAV_INLINE float drwav__bswap_f32(float n)
+{
+ union {
+ drwav_uint32 i;
+ float f;
+ } x;
+ x.f = n;
+ x.i = drwav__bswap32(x.i);
+
+ return x.f;
+}
+
+static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount)
+{
+ drwav_uint64 iSample;
+ for (iSample = 0; iSample < sampleCount; iSample += 1) {
+ pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]);
+ }
+}
+
+
+static DRWAV_INLINE double drwav__bswap_f64(double n)
+{
+ union {
+ drwav_uint64 i;
+ double f;
+ } x;
+ x.f = n;
+ x.i = drwav__bswap64(x.i);
+
+ return x.f;
+}
+
+static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount)
+{
+ drwav_uint64 iSample;
+ for (iSample = 0; iSample < sampleCount; iSample += 1) {
+ pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]);
+ }
+}
+
+
+static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
+{
+ /* Assumes integer PCM. Floating point PCM is done in drwav__bswap_samples_ieee(). */
+ switch (bytesPerSample)
+ {
+ case 2: /* s16, s12 (loosely packed) */
+ {
+ drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
+ } break;
+ case 3: /* s24 */
+ {
+ drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount);
+ } break;
+ case 4: /* s32 */
+ {
+ drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount);
+ } break;
+ default:
+ {
+ /* Unsupported format. */
+ DRWAV_ASSERT(DRWAV_FALSE);
+ } break;
+ }
+}
+
+static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample)
+{
+ switch (bytesPerSample)
+ {
+ #if 0 /* Contributions welcome for f16 support. */
+ case 2: /* f16 */
+ {
+ drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount);
+ } break;
+ #endif
+ case 4: /* f32 */
+ {
+ drwav__bswap_samples_f32((float*)pSamples, sampleCount);
+ } break;
+ case 8: /* f64 */
+ {
+ drwav__bswap_samples_f64((double*)pSamples, sampleCount);
+ } break;
+ default:
+ {
+ /* Unsupported format. */
+ DRWAV_ASSERT(DRWAV_FALSE);
+ } break;
+ }
+}
+
+static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format)
+{
+ switch (format)
+ {
+ case DR_WAVE_FORMAT_PCM:
+ {
+ drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample);
+ } break;
+
+ case DR_WAVE_FORMAT_IEEE_FLOAT:
+ {
+ drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample);
+ } break;
+
+ case DR_WAVE_FORMAT_ALAW:
+ case DR_WAVE_FORMAT_MULAW:
+ {
+ drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount);
+ } break;
+
+ case DR_WAVE_FORMAT_ADPCM:
+ case DR_WAVE_FORMAT_DVI_ADPCM:
+ default:
+ {
+ /* Unsupported format. */
+ DRWAV_ASSERT(DRWAV_FALSE);
+ } break;
+ }
+}
+
+
+static void* drwav__malloc_default(size_t sz, void* pUserData)
+{
+ (void)pUserData;
+ return DRWAV_MALLOC(sz);
+}
+
+static void* drwav__realloc_default(void* p, size_t sz, void* pUserData)
+{
+ (void)pUserData;
+ return DRWAV_REALLOC(p, sz);
+}
+
+static void drwav__free_default(void* p, void* pUserData)
+{
+ (void)pUserData;
+ DRWAV_FREE(p);
+}
+
+
+static void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks == NULL) {
+ return NULL;
+ }
+
+ if (pAllocationCallbacks->onMalloc != NULL) {
+ return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
+ }
+
+ /* Try using realloc(). */
+ if (pAllocationCallbacks->onRealloc != NULL) {
+ return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
+ }
+
+ return NULL;
+}
+
+static void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks == NULL) {
+ return NULL;
+ }
+
+ if (pAllocationCallbacks->onRealloc != NULL) {
+ return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
+ }
+
+ /* Try emulating realloc() in terms of malloc()/free(). */
+ if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
+ void* p2;
+
+ p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
+ if (p2 == NULL) {
+ return NULL;
+ }
+
+ DRWAV_COPY_MEMORY(p2, p, szOld);
+ pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
+
+ return p2;
+ }
+
+ return NULL;
+}
+
+static void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (p == NULL || pAllocationCallbacks == NULL) {
+ return;
+ }
+
+ if (pAllocationCallbacks->onFree != NULL) {
+ pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
+ }
+}
+
+
+drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks != NULL) {
+ /* Copy. */
+ return *pAllocationCallbacks;
+ } else {
+ /* Defaults. */
+ drwav_allocation_callbacks allocationCallbacks;
+ allocationCallbacks.pUserData = NULL;
+ allocationCallbacks.onMalloc = drwav__malloc_default;
+ allocationCallbacks.onRealloc = drwav__realloc_default;
+ allocationCallbacks.onFree = drwav__free_default;
+ return allocationCallbacks;
+ }
+}
+
+
+static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag)
+{
+ return
+ formatTag == DR_WAVE_FORMAT_ADPCM ||
+ formatTag == DR_WAVE_FORMAT_DVI_ADPCM;
+}
+
+static unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize)
+{
+ return (unsigned int)(chunkSize % 2);
+}
+
+static unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize)
+{
+ return (unsigned int)(chunkSize % 8);
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut);
+drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount);
+
+static drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut)
+{
+ if (container == drwav_container_riff) {
+ unsigned char sizeInBytes[4];
+
+ if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
+ return DRWAV_EOF;
+ }
+
+ if (onRead(pUserData, sizeInBytes, 4) != 4) {
+ return DRWAV_INVALID_FILE;
+ }
+
+ pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes);
+ pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
+ *pRunningBytesReadOut += 8;
+ } else {
+ unsigned char sizeInBytes[8];
+
+ if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
+ return DRWAV_EOF;
+ }
+
+ if (onRead(pUserData, sizeInBytes, 8) != 8) {
+ return DRWAV_INVALID_FILE;
+ }
+
+ pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; /* <-- Subtract 24 because w64 includes the size of the header. */
+ pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
+ *pRunningBytesReadOut += 24;
+ }
+
+ return DRWAV_SUCCESS;
+}
+
+static drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
+{
+ drwav_uint64 bytesRemainingToSeek = offset;
+ while (bytesRemainingToSeek > 0) {
+ if (bytesRemainingToSeek > 0x7FFFFFFF) {
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ bytesRemainingToSeek -= 0x7FFFFFFF;
+ } else {
+ if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ bytesRemainingToSeek = 0;
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+static drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData)
+{
+ if (offset <= 0x7FFFFFFF) {
+ return onSeek(pUserData, (int)offset, drwav_seek_origin_start);
+ }
+
+ /* Larger than 32-bit seek. */
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) {
+ return DRWAV_FALSE;
+ }
+ offset -= 0x7FFFFFFF;
+
+ for (;;) {
+ if (offset <= 0x7FFFFFFF) {
+ return onSeek(pUserData, (int)offset, drwav_seek_origin_current);
+ }
+
+ if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ offset -= 0x7FFFFFFF;
+ }
+
+ /* Should never get here. */
+ /*return DRWAV_TRUE; */
+}
+
+
+static drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut)
+{
+ drwav_chunk_header header;
+ unsigned char fmt[16];
+
+ if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
+ return DRWAV_FALSE;
+ }
+
+
+ /* Skip non-fmt chunks. */
+ while ((container == drwav_container_riff && !drwav__fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT))) {
+ if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize;
+
+ /* Try the next header. */
+ if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ /* Validation. */
+ if (container == drwav_container_riff) {
+ if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) {
+ return DRWAV_FALSE;
+ }
+ } else {
+ if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += sizeof(fmt);
+
+ fmtOut->formatTag = drwav__bytes_to_u16(fmt + 0);
+ fmtOut->channels = drwav__bytes_to_u16(fmt + 2);
+ fmtOut->sampleRate = drwav__bytes_to_u32(fmt + 4);
+ fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8);
+ fmtOut->blockAlign = drwav__bytes_to_u16(fmt + 12);
+ fmtOut->bitsPerSample = drwav__bytes_to_u16(fmt + 14);
+
+ fmtOut->extendedSize = 0;
+ fmtOut->validBitsPerSample = 0;
+ fmtOut->channelMask = 0;
+ memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat));
+
+ if (header.sizeInBytes > 16) {
+ unsigned char fmt_cbSize[2];
+ int bytesReadSoFar = 0;
+
+ if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
+ return DRWAV_FALSE; /* Expecting more data. */
+ }
+ *pRunningBytesReadOut += sizeof(fmt_cbSize);
+
+ bytesReadSoFar = 18;
+
+ fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize);
+ if (fmtOut->extendedSize > 0) {
+ /* Simple validation. */
+ if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ if (fmtOut->extendedSize != 22) {
+ return DRWAV_FALSE;
+ }
+ }
+
+ if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ unsigned char fmtext[22];
+ if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) {
+ return DRWAV_FALSE; /* Expecting more data. */
+ }
+
+ fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0);
+ fmtOut->channelMask = drwav__bytes_to_u32(fmtext + 2);
+ drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat);
+ } else {
+ if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ }
+ *pRunningBytesReadOut += fmtOut->extendedSize;
+
+ bytesReadSoFar += fmtOut->extendedSize;
+ }
+
+ /* Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. */
+ if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar);
+ }
+
+ if (header.paddingSize > 0) {
+ if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+ *pRunningBytesReadOut += header.paddingSize;
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor)
+{
+ size_t bytesRead;
+
+ DRWAV_ASSERT(onRead != NULL);
+ DRWAV_ASSERT(pCursor != NULL);
+
+ bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
+ *pCursor += bytesRead;
+ return bytesRead;
+}
+
+drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor)
+{
+ DRWAV_ASSERT(onSeek != NULL);
+ DRWAV_ASSERT(pCursor != NULL);
+
+ if (!onSeek(pUserData, offset, origin)) {
+ return DRWAV_FALSE;
+ }
+
+ if (origin == drwav_seek_origin_start) {
+ *pCursor = offset;
+ } else {
+ *pCursor += offset;
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+
+static drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav)
+{
+ /*
+ The bytes per frame is a bit ambiguous. It can be either be based on the bits per sample, or the block align. The way I'm doing it here
+ is that if the bits per sample is a multiple of 8, use floor(bitsPerSample*channels/8), otherwise fall back to the block align.
+ */
+ if ((pWav->bitsPerSample & 0x7) == 0) {
+ /* Bits per sample is a multiple of 8. */
+ return (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
+ } else {
+ return pWav->fmt.blockAlign;
+ }
+}
+
+
+drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pWav == NULL || onRead == NULL || onSeek == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
+ pWav->onRead = onRead;
+ pWav->onSeek = onSeek;
+ pWav->pUserData = pReadSeekUserData;
+ pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
+
+ if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
+ return DRWAV_FALSE; /* Invalid allocation callbacks. */
+ }
+
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags)
+{
+ /* This function assumes drwav_preinit() has been called beforehand. */
+
+ drwav_uint64 cursor; /* <-- Keeps track of the byte position so we can seek to specific locations. */
+ drwav_bool32 sequential;
+ unsigned char riff[4];
+ drwav_fmt fmt;
+ unsigned short translatedFormatTag;
+ drwav_uint64 sampleCountFromFactChunk;
+ drwav_bool32 foundDataChunk;
+ drwav_uint64 dataChunkSize;
+ drwav_uint64 chunkSize;
+
+ cursor = 0;
+ sequential = (flags & DRWAV_SEQUENTIAL) != 0;
+
+ /* The first 4 bytes should be the RIFF identifier. */
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
+ return DRWAV_FALSE;
+ }
+
+ /*
+ The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for
+ w64 it will start with "riff".
+ */
+ if (drwav__fourcc_equal(riff, "RIFF")) {
+ pWav->container = drwav_container_riff;
+ } else if (drwav__fourcc_equal(riff, "riff")) {
+ int i;
+ drwav_uint8 riff2[12];
+
+ pWav->container = drwav_container_w64;
+
+ /* Check the rest of the GUID for validity. */
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
+ return DRWAV_FALSE;
+ }
+
+ for (i = 0; i < 12; ++i) {
+ if (riff2[i] != drwavGUID_W64_RIFF[i+4]) {
+ return DRWAV_FALSE;
+ }
+ }
+ } else {
+ return DRWAV_FALSE; /* Unknown or unsupported container. */
+ }
+
+
+ if (pWav->container == drwav_container_riff) {
+ unsigned char chunkSizeBytes[4];
+ unsigned char wave[4];
+
+ /* RIFF/WAVE */
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__bytes_to_u32(chunkSizeBytes) < 36) {
+ return DRWAV_FALSE; /* Chunk size should always be at least 36 bytes. */
+ }
+
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
+ return DRWAV_FALSE;
+ }
+
+ if (!drwav__fourcc_equal(wave, "WAVE")) {
+ return DRWAV_FALSE; /* Expecting "WAVE". */
+ }
+ } else {
+ unsigned char chunkSizeBytes[8];
+ drwav_uint8 wave[16];
+
+ /* W64 */
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__bytes_to_u64(chunkSizeBytes) < 80) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
+ return DRWAV_FALSE;
+ }
+
+ if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ /* The next bytes should be the "fmt " chunk. */
+ if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) {
+ return DRWAV_FALSE; /* Failed to read the "fmt " chunk. */
+ }
+
+ /* Basic validation. */
+ if (fmt.sampleRate == 0 || fmt.channels == 0 || fmt.bitsPerSample == 0 || fmt.blockAlign == 0) {
+ return DRWAV_FALSE; /* Invalid channel count. Probably an invalid WAV file. */
+ }
+
+
+ /* Translate the internal format. */
+ translatedFormatTag = fmt.formatTag;
+ if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) {
+ translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0);
+ }
+
+
+
+ sampleCountFromFactChunk = 0;
+
+ /*
+ We need to enumerate over each chunk for two reasons:
+ 1) The "data" chunk may not be the next one
+ 2) We may want to report each chunk back to the client
+
+ In order to correctly report each chunk back to the client we will need to keep looping until the end of the file.
+ */
+ foundDataChunk = DRWAV_FALSE;
+ dataChunkSize = 0;
+
+ /* The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. */
+ for (;;)
+ {
+ drwav_chunk_header header;
+ drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
+ if (result != DRWAV_SUCCESS) {
+ if (!foundDataChunk) {
+ return DRWAV_FALSE;
+ } else {
+ break; /* Probably at the end of the file. Get out of the loop. */
+ }
+ }
+
+ /* Tell the client about this chunk. */
+ if (!sequential && onChunk != NULL) {
+ drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header);
+
+ /*
+ dr_wav may need to read the contents of the chunk, so we now need to seek back to the position before
+ we called the callback.
+ */
+ if (callbackBytesRead > 0) {
+ if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) {
+ return DRWAV_FALSE;
+ }
+ }
+ }
+
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+
+ chunkSize = header.sizeInBytes;
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "data")) {
+ foundDataChunk = DRWAV_TRUE;
+ dataChunkSize = chunkSize;
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) {
+ foundDataChunk = DRWAV_TRUE;
+ dataChunkSize = chunkSize;
+ }
+ }
+
+ /*
+ If at this point we have found the data chunk and we're running in sequential mode, we need to break out of this loop. The reason for
+ this is that we would otherwise require a backwards seek which sequential mode forbids.
+ */
+ if (foundDataChunk && sequential) {
+ break;
+ }
+
+ /* Optional. Get the total sample count from the FACT chunk. This is useful for compressed formats. */
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "fact")) {
+ drwav_uint32 sampleCount;
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
+ return DRWAV_FALSE;
+ }
+ chunkSize -= 4;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+
+ /*
+ The sample count in the "fact" chunk is either unreliable, or I'm not understanding it properly. For now I am only enabling this
+ for Microsoft ADPCM formats.
+ */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ sampleCountFromFactChunk = sampleCount;
+ } else {
+ sampleCountFromFactChunk = 0;
+ }
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_FACT)) {
+ if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
+ return DRWAV_FALSE;
+ }
+ chunkSize -= 8;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+ }
+ }
+
+ /* "smpl" chunk. */
+ if (pWav->container == drwav_container_riff) {
+ if (drwav__fourcc_equal(header.id.fourcc, "smpl")) {
+ unsigned char smplHeaderData[36]; /* 36 = size of the smpl header section, not including the loop data. */
+ if (chunkSize >= sizeof(smplHeaderData)) {
+ drwav_uint64 bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplHeaderData, sizeof(smplHeaderData), &cursor);
+ chunkSize -= bytesJustRead;
+
+ if (bytesJustRead == sizeof(smplHeaderData)) {
+ drwav_uint32 iLoop;
+
+ pWav->smpl.manufacturer = drwav__bytes_to_u32(smplHeaderData+0);
+ pWav->smpl.product = drwav__bytes_to_u32(smplHeaderData+4);
+ pWav->smpl.samplePeriod = drwav__bytes_to_u32(smplHeaderData+8);
+ pWav->smpl.midiUnityNotes = drwav__bytes_to_u32(smplHeaderData+12);
+ pWav->smpl.midiPitchFraction = drwav__bytes_to_u32(smplHeaderData+16);
+ pWav->smpl.smpteFormat = drwav__bytes_to_u32(smplHeaderData+20);
+ pWav->smpl.smpteOffset = drwav__bytes_to_u32(smplHeaderData+24);
+ pWav->smpl.numSampleLoops = drwav__bytes_to_u32(smplHeaderData+28);
+ pWav->smpl.samplerData = drwav__bytes_to_u32(smplHeaderData+32);
+
+ for (iLoop = 0; iLoop < pWav->smpl.numSampleLoops && iLoop < drwav_countof(pWav->smpl.loops); ++iLoop) {
+ unsigned char smplLoopData[24]; /* 24 = size of a loop section in the smpl chunk. */
+ bytesJustRead = drwav__on_read(pWav->onRead, pWav->pUserData, smplLoopData, sizeof(smplLoopData), &cursor);
+ chunkSize -= bytesJustRead;
+
+ if (bytesJustRead == sizeof(smplLoopData)) {
+ pWav->smpl.loops[iLoop].cuePointId = drwav__bytes_to_u32(smplLoopData+0);
+ pWav->smpl.loops[iLoop].type = drwav__bytes_to_u32(smplLoopData+4);
+ pWav->smpl.loops[iLoop].start = drwav__bytes_to_u32(smplLoopData+8);
+ pWav->smpl.loops[iLoop].end = drwav__bytes_to_u32(smplLoopData+12);
+ pWav->smpl.loops[iLoop].fraction = drwav__bytes_to_u32(smplLoopData+16);
+ pWav->smpl.loops[iLoop].playCount = drwav__bytes_to_u32(smplLoopData+20);
+ } else {
+ break; /* Break from the smpl loop for loop. */
+ }
+ }
+ }
+ } else {
+ /* Looks like invalid data. Ignore the chunk. */
+ }
+ }
+ } else {
+ if (drwav__guid_equal(header.id.guid, drwavGUID_W64_SMPL)) {
+ /*
+ This path will be hit when a W64 WAV file contains a smpl chunk. I don't have a sample file to test this path, so a contribution
+ is welcome to add support for this.
+ */
+ }
+ }
+
+ /* Make sure we seek past the padding. */
+ chunkSize += header.paddingSize;
+ if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) {
+ break;
+ }
+ cursor += chunkSize;
+
+ if (!foundDataChunk) {
+ pWav->dataChunkDataPos = cursor;
+ }
+ }
+
+ /* If we haven't found a data chunk, return an error. */
+ if (!foundDataChunk) {
+ return DRWAV_FALSE;
+ }
+
+ /* We may have moved passed the data chunk. If so we need to move back. If running in sequential mode we can assume we are already sitting on the data chunk. */
+ if (!sequential) {
+ if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
+ return DRWAV_FALSE;
+ }
+ cursor = pWav->dataChunkDataPos;
+ }
+
+
+ /* At this point we should be sitting on the first byte of the raw audio data. */
+
+ pWav->fmt = fmt;
+ pWav->sampleRate = fmt.sampleRate;
+ pWav->channels = fmt.channels;
+ pWav->bitsPerSample = fmt.bitsPerSample;
+ pWav->bytesRemaining = dataChunkSize;
+ pWav->translatedFormatTag = translatedFormatTag;
+ pWav->dataChunkDataSize = dataChunkSize;
+
+ if (sampleCountFromFactChunk != 0) {
+ pWav->totalPCMFrameCount = sampleCountFromFactChunk;
+ } else {
+ pWav->totalPCMFrameCount = dataChunkSize / drwav_get_bytes_per_pcm_frame(pWav);
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ drwav_uint64 totalBlockHeaderSizeInBytes;
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+
+ /* Make sure any trailing partial block is accounted for. */
+ if ((blockCount * fmt.blockAlign) < dataChunkSize) {
+ blockCount += 1;
+ }
+
+ /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */
+ totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
+ pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
+ }
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ drwav_uint64 totalBlockHeaderSizeInBytes;
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+
+ /* Make sure any trailing partial block is accounted for. */
+ if ((blockCount * fmt.blockAlign) < dataChunkSize) {
+ blockCount += 1;
+ }
+
+ /* We decode two samples per byte. There will be blockCount headers in the data chunk. This is enough to know how to calculate the total PCM frame count. */
+ totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
+ pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
+
+ /* The header includes a decoded sample for each channel which acts as the initial predictor sample. */
+ pWav->totalPCMFrameCount += blockCount;
+ }
+ }
+
+ /* Some formats only support a certain number of channels. */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ if (pWav->channels > 2) {
+ return DRWAV_FALSE;
+ }
+ }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+ /*
+ I use libsndfile as a benchmark for testing, however in the version I'm using (from the Windows installer on the libsndfile website),
+ it appears the total sample count libsndfile uses for MS-ADPCM is incorrect. It would seem they are computing the total sample count
+ from the number of blocks, however this results in the inclusion of extra silent samples at the end of the last block. The correct
+ way to know the total sample count is to inspect the "fact" chunk, which should always be present for compressed formats, and should
+ always include the sample count. This little block of code below is only used to emulate the libsndfile logic so I can properly run my
+ correctness tests against libsndfile, and is disabled by default.
+ */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; /* x2 because two samples per byte. */
+ }
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign;
+ pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
+ }
+#endif
+
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
+}
+
+
+static drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize)
+{
+ drwav_uint32 dataSubchunkPaddingSize = drwav__chunk_padding_size_riff(dataChunkSize);
+
+ if (dataChunkSize <= (0xFFFFFFFFUL - 36 - dataSubchunkPaddingSize)) {
+ return 36 + (drwav_uint32)(dataChunkSize + dataSubchunkPaddingSize);
+ } else {
+ return 0xFFFFFFFF;
+ }
+}
+
+static drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize)
+{
+ if (dataChunkSize <= 0xFFFFFFFFUL) {
+ return (drwav_uint32)dataChunkSize;
+ } else {
+ return 0xFFFFFFFFUL;
+ }
+}
+
+static drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize)
+{
+ drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize);
+
+ return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; /* +24 because W64 includes the size of the GUID and size fields. */
+}
+
+static drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize)
+{
+ return 24 + dataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
+}
+
+
+drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pWav == NULL || onWrite == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ if (!isSequential && onSeek == NULL) {
+ return DRWAV_FALSE; /* <-- onSeek is required when in non-sequential mode. */
+ }
+
+ /* Not currently supporting compressed formats. Will need to add support for the "fact" chunk before we enable this. */
+ if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) {
+ return DRWAV_FALSE;
+ }
+ if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return DRWAV_FALSE;
+ }
+
+ DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav));
+ pWav->onWrite = onWrite;
+ pWav->onSeek = onSeek;
+ pWav->pUserData = pUserData;
+ pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
+
+ if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
+ return DRWAV_FALSE; /* Invalid allocation callbacks. */
+ }
+
+ pWav->fmt.formatTag = (drwav_uint16)pFormat->format;
+ pWav->fmt.channels = (drwav_uint16)pFormat->channels;
+ pWav->fmt.sampleRate = pFormat->sampleRate;
+ pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
+ pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
+ pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+ pWav->fmt.extendedSize = 0;
+ pWav->isSequentialWrite = isSequential;
+
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount)
+{
+ /* The function assumes drwav_preinit_write() was called beforehand. */
+
+ size_t runningPos = 0;
+ drwav_uint64 initialDataChunkSize = 0;
+ drwav_uint64 chunkSizeFMT;
+
+ /*
+ The initial values for the "RIFF" and "data" chunks depends on whether or not we are initializing in sequential mode or not. In
+ sequential mode we set this to its final values straight away since they can be calculated from the total sample count. In non-
+ sequential mode we initialize it all to zero and fill it out in drwav_uninit() using a backwards seek.
+ */
+ if (pWav->isSequentialWrite) {
+ initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
+
+ /*
+ The RIFF container has a limit on the number of samples. drwav is not allowing this. There's no practical limits for Wave64
+ so for the sake of simplicity I'm not doing any validation for that.
+ */
+ if (pFormat->container == drwav_container_riff) {
+ if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
+ return DRWAV_FALSE; /* Not enough room to store every sample. */
+ }
+ }
+ }
+
+ pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
+
+
+ /* "RIFF" chunk. */
+ if (pFormat->container == drwav_container_riff) {
+ drwav_uint32 chunkSizeRIFF = 36 + (drwav_uint32)initialDataChunkSize; /* +36 = "RIFF"+[RIFF Chunk Size]+"WAVE" + [sizeof "fmt " chunk] */
+ runningPos += pWav->onWrite(pWav->pUserData, "RIFF", 4);
+ runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 4);
+ runningPos += pWav->onWrite(pWav->pUserData, "WAVE", 4);
+ } else {
+ drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
+ runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_RIFF, 16);
+ runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeRIFF, 8);
+ runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_WAVE, 16);
+ }
+
+ /* "fmt " chunk. */
+ if (pFormat->container == drwav_container_riff) {
+ chunkSizeFMT = 16;
+ runningPos += pWav->onWrite(pWav->pUserData, "fmt ", 4);
+ runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 4);
+ } else {
+ chunkSizeFMT = 40;
+ runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_FMT, 16);
+ runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeFMT, 8);
+ }
+
+ runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.formatTag, 2);
+ runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.channels, 2);
+ runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.sampleRate, 4);
+ runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.avgBytesPerSec, 4);
+ runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.blockAlign, 2);
+ runningPos += pWav->onWrite(pWav->pUserData, &pWav->fmt.bitsPerSample, 2);
+
+ pWav->dataChunkDataPos = runningPos;
+
+ /* "data" chunk. */
+ if (pFormat->container == drwav_container_riff) {
+ drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize;
+ runningPos += pWav->onWrite(pWav->pUserData, "data", 4);
+ runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 4);
+ } else {
+ drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; /* +24 because W64 includes the size of the GUID and size fields. */
+ runningPos += pWav->onWrite(pWav->pUserData, drwavGUID_W64_DATA, 16);
+ runningPos += pWav->onWrite(pWav->pUserData, &chunkSizeDATA, 8);
+ }
+
+
+ /* Simple validation. */
+ if (pFormat->container == drwav_container_riff) {
+ if (runningPos != 20 + chunkSizeFMT + 8) {
+ return DRWAV_FALSE;
+ }
+ } else {
+ if (runningPos != 40 + chunkSizeFMT + 24) {
+ return DRWAV_FALSE;
+ }
+ }
+
+
+ /* Set some properties for the client's convenience. */
+ pWav->container = pFormat->container;
+ pWav->channels = (drwav_uint16)pFormat->channels;
+ pWav->sampleRate = pFormat->sampleRate;
+ pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample;
+ pWav->translatedFormatTag = (drwav_uint16)pFormat->format;
+
+ return DRWAV_TRUE;
+}
+
+
+drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_write__internal(pWav, pFormat, 0); /* DRWAV_FALSE = Not Sequential */
+}
+
+drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount); /* DRWAV_TRUE = Sequential */
+}
+
+drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pFormat == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
+}
+
+drwav_uint64 drwav_target_write_size_bytes(drwav_data_format const *format, drwav_uint64 totalSampleCount)
+{
+ drwav_uint64 targetDataSizeBytes = (totalSampleCount * format->channels * format->bitsPerSample/8);
+ drwav_uint64 riffChunkSizeBytes;
+ drwav_uint64 fileSizeBytes;
+
+ if (format->container == drwav_container_riff) {
+ riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes);
+ fileSizeBytes = (8 + riffChunkSizeBytes); /* +8 because WAV doesn't include the size of the ChunkID and ChunkSize fields. */
+ } else {
+ riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes);
+ fileSizeBytes = riffChunkSizeBytes;
+ }
+
+ return fileSizeBytes;
+}
+
+
+#ifndef DR_WAV_NO_STDIO
+FILE* drwav_fopen(const char* filePath, const char* openMode)
+{
+ FILE* pFile;
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (fopen_s(&pFile, filePath, openMode) != 0) {
+ return NULL;
+ }
+#else
+ pFile = fopen(filePath, openMode);
+ if (pFile == NULL) {
+ return NULL;
+ }
+#endif
+
+ return pFile;
+}
+
+FILE* drwav_wfopen(const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ FILE* pFile;
+
+#if defined(_WIN32)
+ (void)pAllocationCallbacks;
+ #if defined(_MSC_VER) && _MSC_VER >= 1400
+ if (_wfopen_s(&pFile, pFilePath, pOpenMode) != 0) {
+ return NULL;
+ }
+ #else
+ pFile = _wfopen(pFilePath, pOpenMode);
+ if (pFile == NULL) {
+ return NULL;
+ }
+ #endif
+#else
+ /*
+ Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
+ think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
+ maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
+ */
+ {
+ mbstate_t mbs;
+ size_t lenMB;
+ const wchar_t* pFilePathTemp = pFilePath;
+ char* pFilePathMB = NULL;
+ const wchar_t* pOpenModeMBTemp = pOpenMode;
+ char pOpenModeMB[16];
+ drwav_allocation_callbacks allocationCallbacks;
+
+ allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
+
+ /* Get the length first. */
+ DRWAV_ZERO_MEMORY(&mbs, sizeof(mbs));
+ lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
+ if (lenMB == (size_t)-1) {
+ return NULL;
+ }
+
+ pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, &allocationCallbacks);
+ if (pFilePathMB == NULL) {
+ return NULL;
+ }
+
+ pFilePathTemp = pFilePath;
+ DRWAV_ZERO_MEMORY(&mbs, sizeof(mbs));
+ wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
+
+ DRWAV_ZERO_MEMORY(&mbs, sizeof(mbs));
+ wcsrtombs(pOpenModeMB, &pOpenModeMBTemp, sizeof(pOpenModeMB), &mbs);
+
+ pFile = fopen(pFilePathMB, pOpenModeMB);
+
+ drwav__free_from_callbacks(pFilePathMB, &allocationCallbacks);
+ }
+#endif
+
+ return pFile;
+}
+
+
+static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
+}
+
+static size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
+{
+ return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
+}
+
+static drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
+}
+
+drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
+}
+
+
+drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (!drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks)) {
+ fclose(pFile);
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
+}
+
+drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ FILE* pFile = drwav_fopen(filename, "rb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ /* This takes ownership of the FILE* object. */
+ return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ FILE* pFile = drwav_wfopen(filename, L"rb", pAllocationCallbacks);
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ /* This takes ownership of the FILE* object. */
+ return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
+}
+
+
+drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks)) {
+ fclose(pFile);
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
+}
+
+drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ FILE* pFile = drwav_fopen(filename, "wb");
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ /* This takes ownership of the FILE* object. */
+ return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ FILE* pFile = drwav_wfopen(filename, L"wb", pAllocationCallbacks);
+ if (pFile == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ /* This takes ownership of the FILE* object. */
+ return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pFormat == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pFormat == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
+}
+#endif /* DR_WAV_NO_STDIO */
+
+
+static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
+{
+ drwav* pWav = (drwav*)pUserData;
+ size_t bytesRemaining;
+
+ DRWAV_ASSERT(pWav != NULL);
+ DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
+
+ bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
+ if (bytesToRead > bytesRemaining) {
+ bytesToRead = bytesRemaining;
+ }
+
+ if (bytesToRead > 0) {
+ DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
+ pWav->memoryStream.currentReadPos += bytesToRead;
+ }
+
+ return bytesToRead;
+}
+
+static drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ drwav* pWav = (drwav*)pUserData;
+ DRWAV_ASSERT(pWav != NULL);
+
+ if (origin == drwav_seek_origin_current) {
+ if (offset > 0) {
+ if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) {
+ return DRWAV_FALSE; /* Trying to seek too far forward. */
+ }
+ } else {
+ if (pWav->memoryStream.currentReadPos < (size_t)-offset) {
+ return DRWAV_FALSE; /* Trying to seek too far backwards. */
+ }
+ }
+
+ /* This will never underflow thanks to the clamps above. */
+ pWav->memoryStream.currentReadPos += offset;
+ } else {
+ if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) {
+ pWav->memoryStream.currentReadPos = offset;
+ } else {
+ return DRWAV_FALSE; /* Trying to seek too far forward. */
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+static size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
+{
+ drwav* pWav = (drwav*)pUserData;
+ size_t bytesRemaining;
+
+ DRWAV_ASSERT(pWav != NULL);
+ DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
+
+ bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
+ if (bytesRemaining < bytesToWrite) {
+ /* Need to reallocate. */
+ void* pNewData;
+ size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
+
+ /* If doubling wasn't enough, just make it the minimum required size to write the data. */
+ if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
+ newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
+ }
+
+ pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
+ if (pNewData == NULL) {
+ return 0;
+ }
+
+ *pWav->memoryStreamWrite.ppData = pNewData;
+ pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
+ }
+
+ DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
+
+ pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
+ if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
+ pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
+ }
+
+ *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
+
+ return bytesToWrite;
+}
+
+static drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ drwav* pWav = (drwav*)pUserData;
+ DRWAV_ASSERT(pWav != NULL);
+
+ if (origin == drwav_seek_origin_current) {
+ if (offset > 0) {
+ if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) {
+ offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); /* Trying to seek too far forward. */
+ }
+ } else {
+ if (pWav->memoryStreamWrite.currentWritePos < (size_t)-offset) {
+ offset = -(int)pWav->memoryStreamWrite.currentWritePos; /* Trying to seek too far backwards. */
+ }
+ }
+
+ /* This will never underflow thanks to the clamps above. */
+ pWav->memoryStreamWrite.currentWritePos += offset;
+ } else {
+ if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) {
+ pWav->memoryStreamWrite.currentWritePos = offset;
+ } else {
+ pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; /* Trying to seek too far forward. */
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (data == NULL || dataSize == 0) {
+ return DRWAV_FALSE;
+ }
+
+ if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->memoryStream.data = (const unsigned char*)data;
+ pWav->memoryStream.dataSize = dataSize;
+ pWav->memoryStream.currentReadPos = 0;
+
+ return drwav_init__internal(pWav, onChunk, pChunkUserData, flags);
+}
+
+
+drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (ppData == NULL || pDataSize == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ *ppData = NULL; /* Important because we're using realloc()! */
+ *pDataSize = 0;
+
+ if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->memoryStreamWrite.ppData = ppData;
+ pWav->memoryStreamWrite.pDataSize = pDataSize;
+ pWav->memoryStreamWrite.dataSize = 0;
+ pWav->memoryStreamWrite.dataCapacity = 0;
+ pWav->memoryStreamWrite.currentWritePos = 0;
+
+ return drwav_init_write__internal(pWav, pFormat, totalSampleCount);
+}
+
+drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks);
+}
+
+drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pFormat == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
+}
+
+
+
+drwav_result drwav_uninit(drwav* pWav)
+{
+ drwav_result result = DRWAV_SUCCESS;
+
+ if (pWav == NULL) {
+ return DRWAV_INVALID_ARGS;
+ }
+
+ /*
+ If the drwav object was opened in write mode we'll need to finalize a few things:
+ - Make sure the "data" chunk is aligned to 16-bits for RIFF containers, or 64 bits for W64 containers.
+ - Set the size of the "data" chunk.
+ */
+ if (pWav->onWrite != NULL) {
+ drwav_uint32 paddingSize = 0;
+
+ /* Padding. Do not adjust pWav->dataChunkDataSize - this should not include the padding. */
+ if (pWav->container == drwav_container_riff) {
+ paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize);
+ } else {
+ paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize);
+ }
+
+ if (paddingSize > 0) {
+ drwav_uint64 paddingData = 0;
+ pWav->onWrite(pWav->pUserData, &paddingData, paddingSize);
+ }
+
+ /*
+ Chunk sizes. When using sequential mode, these will have been filled in at initialization time. We only need
+ to do this when using non-sequential mode.
+ */
+ if (pWav->onSeek && !pWav->isSequentialWrite) {
+ if (pWav->container == drwav_container_riff) {
+ /* The "RIFF" chunk size. */
+ if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) {
+ drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &riffChunkSize, 4);
+ }
+
+ /* the "data" chunk size. */
+ if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 4, drwav_seek_origin_start)) {
+ drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &dataChunkSize, 4);
+ }
+ } else {
+ /* The "RIFF" chunk size. */
+ if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) {
+ drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &riffChunkSize, 8);
+ }
+
+ /* The "data" chunk size. */
+ if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos + 16, drwav_seek_origin_start)) {
+ drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize);
+ pWav->onWrite(pWav->pUserData, &dataChunkSize, 8);
+ }
+ }
+ }
+
+ /* Validation for sequential mode. */
+ if (pWav->isSequentialWrite) {
+ if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
+ result = DRWAV_INVALID_FILE;
+ }
+ }
+ }
+
+#ifndef DR_WAV_NO_STDIO
+ /*
+ If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file()
+ was used by looking at the onRead and onSeek callbacks.
+ */
+ if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) {
+ fclose((FILE*)pWav->pUserData);
+ }
+#endif
+
+ return result;
+}
+
+
+
+size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut)
+{
+ size_t bytesRead;
+
+ if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ if (bytesToRead > pWav->bytesRemaining) {
+ bytesToRead = (size_t)pWav->bytesRemaining;
+ }
+
+ bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
+
+ pWav->bytesRemaining -= bytesRead;
+ return bytesRead;
+}
+
+
+
+drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
+{
+ drwav_uint32 bytesPerFrame;
+
+ if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ /* Cannot use this function for compressed formats. */
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ return 0;
+ }
+
+ bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (framesToRead * bytesPerFrame > DRWAV_SIZE_MAX) {
+ framesToRead = DRWAV_SIZE_MAX / bytesPerFrame;
+ }
+
+ return drwav_read_raw(pWav, (size_t)(framesToRead * bytesPerFrame), pBufferOut) / bytesPerFrame;
+}
+
+drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
+{
+ drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
+ drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, drwav_get_bytes_per_pcm_frame(pWav)/pWav->channels, pWav->translatedFormatTag);
+
+ return framesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut)
+{
+ if (drwav__is_little_endian()) {
+ return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
+ } else {
+ return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
+ }
+}
+
+
+
+drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav)
+{
+ if (pWav->onWrite != NULL) {
+ return DRWAV_FALSE; /* No seeking in write mode. */
+ }
+
+ if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) {
+ return DRWAV_FALSE;
+ }
+
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ pWav->compressed.iCurrentPCMFrame = 0;
+ }
+
+ pWav->bytesRemaining = pWav->dataChunkDataSize;
+ return DRWAV_TRUE;
+}
+
+drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex)
+{
+ /* Seeking should be compatible with wave files > 2GB. */
+
+ if (pWav->onWrite != NULL) {
+ return DRWAV_FALSE; /* No seeking in write mode. */
+ }
+
+ if (pWav == NULL || pWav->onSeek == NULL) {
+ return DRWAV_FALSE;
+ }
+
+ /* If there are no samples, just return DRWAV_TRUE without doing anything. */
+ if (pWav->totalPCMFrameCount == 0) {
+ return DRWAV_TRUE;
+ }
+
+ /* Make sure the sample is clamped. */
+ if (targetFrameIndex >= pWav->totalPCMFrameCount) {
+ targetFrameIndex = pWav->totalPCMFrameCount - 1;
+ }
+
+ /*
+ For compressed formats we just use a slow generic seek. If we are seeking forward we just seek forward. If we are going backwards we need
+ to seek back to the start.
+ */
+ if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) {
+ /* TODO: This can be optimized. */
+
+ /*
+ If we're seeking forward it's simple - just keep reading samples until we hit the sample we're requesting. If we're seeking backwards,
+ we first need to seek back to the start and then just do the same thing as a forward seek.
+ */
+ if (targetFrameIndex < pWav->compressed.iCurrentPCMFrame) {
+ if (!drwav_seek_to_first_pcm_frame(pWav)) {
+ return DRWAV_FALSE;
+ }
+ }
+
+ if (targetFrameIndex > pWav->compressed.iCurrentPCMFrame) {
+ drwav_uint64 offsetInFrames = targetFrameIndex - pWav->compressed.iCurrentPCMFrame;
+
+ drwav_int16 devnull[2048];
+ while (offsetInFrames > 0) {
+ drwav_uint64 framesRead = 0;
+ drwav_uint64 framesToRead = offsetInFrames;
+ if (framesToRead > drwav_countof(devnull)/pWav->channels) {
+ framesToRead = drwav_countof(devnull)/pWav->channels;
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
+ } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
+ } else {
+ assert(DRWAV_FALSE); /* If this assertion is triggered it means I've implemented a new compressed format but forgot to add a branch for it here. */
+ }
+
+ if (framesRead != framesToRead) {
+ return DRWAV_FALSE;
+ }
+
+ offsetInFrames -= framesRead;
+ }
+ }
+ } else {
+ drwav_uint64 totalSizeInBytes;
+ drwav_uint64 currentBytePos;
+ drwav_uint64 targetBytePos;
+ drwav_uint64 offset;
+
+ totalSizeInBytes = pWav->totalPCMFrameCount * drwav_get_bytes_per_pcm_frame(pWav);
+ DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining);
+
+ currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
+ targetBytePos = targetFrameIndex * drwav_get_bytes_per_pcm_frame(pWav);
+
+ if (currentBytePos < targetBytePos) {
+ /* Offset forwards. */
+ offset = (targetBytePos - currentBytePos);
+ } else {
+ /* Offset backwards. */
+ if (!drwav_seek_to_first_pcm_frame(pWav)) {
+ return DRWAV_FALSE;
+ }
+ offset = targetBytePos;
+ }
+
+ while (offset > 0) {
+ int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
+ if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) {
+ return DRWAV_FALSE;
+ }
+
+ pWav->bytesRemaining -= offset32;
+ offset -= offset32;
+ }
+ }
+
+ return DRWAV_TRUE;
+}
+
+
+size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData)
+{
+ size_t bytesWritten;
+
+ if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
+ return 0;
+ }
+
+ bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
+ pWav->dataChunkDataSize += bytesWritten;
+
+ return bytesWritten;
+}
+
+
+drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
+{
+ drwav_uint64 bytesToWrite;
+ drwav_uint64 bytesWritten;
+ const drwav_uint8* pRunningData;
+
+ if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
+ return 0;
+ }
+
+ bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
+ if (bytesToWrite > DRWAV_SIZE_MAX) {
+ return 0;
+ }
+
+ bytesWritten = 0;
+ pRunningData = (const drwav_uint8*)pData;
+
+ while (bytesToWrite > 0) {
+ size_t bytesJustWritten;
+ drwav_uint64 bytesToWriteThisIteration = bytesToWrite;
+ if (bytesToWriteThisIteration > DRWAV_SIZE_MAX) {
+ bytesToWriteThisIteration = DRWAV_SIZE_MAX;
+ }
+
+ bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
+ if (bytesJustWritten == 0) {
+ break;
+ }
+
+ bytesToWrite -= bytesJustWritten;
+ bytesWritten += bytesJustWritten;
+ pRunningData += bytesJustWritten;
+ }
+
+ return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
+}
+
+drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
+{
+ drwav_uint64 bytesToWrite;
+ drwav_uint64 bytesWritten;
+ drwav_uint32 bytesPerSample;
+ const drwav_uint8* pRunningData;
+
+ if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
+ return 0;
+ }
+
+ bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
+ if (bytesToWrite > DRWAV_SIZE_MAX) {
+ return 0;
+ }
+
+ bytesWritten = 0;
+ pRunningData = (const drwav_uint8*)pData;
+
+ bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
+
+ while (bytesToWrite > 0) {
+ drwav_uint8 temp[4096];
+ drwav_uint32 sampleCount;
+ size_t bytesJustWritten;
+ drwav_uint64 bytesToWriteThisIteration;
+
+ bytesToWriteThisIteration = bytesToWrite;
+ if (bytesToWriteThisIteration > DRWAV_SIZE_MAX) {
+ bytesToWriteThisIteration = DRWAV_SIZE_MAX;
+ }
+
+ /*
+ WAV files are always little-endian. We need to byte swap on big-endian architectures. Since our input buffer is read-only we need
+ to use an intermediary buffer for the conversion.
+ */
+ sampleCount = sizeof(temp)/bytesPerSample;
+
+ if (bytesToWriteThisIteration > sampleCount*bytesPerSample) {
+ bytesToWriteThisIteration = sampleCount*bytesPerSample;
+ }
+
+ DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
+ drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag);
+
+ bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
+ if (bytesJustWritten == 0) {
+ break;
+ }
+
+ bytesToWrite -= bytesJustWritten;
+ bytesWritten += bytesJustWritten;
+ pRunningData += bytesJustWritten;
+ }
+
+ return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
+}
+
+drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData)
+{
+ if (drwav__is_little_endian()) {
+ return drwav_write_pcm_frames_le(pWav, framesToWrite, pData);
+ } else {
+ return drwav_write_pcm_frames_be(pWav, framesToWrite, pData);
+ }
+}
+
+
+drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalFramesRead = 0;
+
+ DRWAV_ASSERT(pWav != NULL);
+ DRWAV_ASSERT(framesToRead > 0);
+ DRWAV_ASSERT(pBufferOut != NULL);
+
+ /* TODO: Lots of room for optimization here. */
+
+ while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
+ /* If there are no cached frames we need to load a new block. */
+ if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
+ if (pWav->channels == 1) {
+ /* Mono. */
+ drwav_uint8 header[7];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalFramesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->msadpcm.predictor[0] = header[0];
+ pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 1);
+ pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 3);
+ pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 5);
+ pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
+ pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
+ pWav->msadpcm.cachedFrameCount = 2;
+ } else {
+ /* Stereo. */
+ drwav_uint8 header[14];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalFramesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->msadpcm.predictor[0] = header[0];
+ pWav->msadpcm.predictor[1] = header[1];
+ pWav->msadpcm.delta[0] = drwav__bytes_to_s16(header + 2);
+ pWav->msadpcm.delta[1] = drwav__bytes_to_s16(header + 4);
+ pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav__bytes_to_s16(header + 6);
+ pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav__bytes_to_s16(header + 8);
+ pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav__bytes_to_s16(header + 10);
+ pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav__bytes_to_s16(header + 12);
+
+ pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
+ pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
+ pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
+ pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
+ pWav->msadpcm.cachedFrameCount = 2;
+ }
+ }
+
+ /* Output anything that's cached. */
+ while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
+ drwav_uint32 iSample = 0;
+ for (iSample = 0; iSample < pWav->channels; iSample += 1) {
+ pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
+ }
+
+ pBufferOut += pWav->channels;
+ framesToRead -= 1;
+ totalFramesRead += 1;
+ pWav->compressed.iCurrentPCMFrame += 1;
+ pWav->msadpcm.cachedFrameCount -= 1;
+ }
+
+ if (framesToRead == 0) {
+ return totalFramesRead;
+ }
+
+
+ /*
+ If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ loop iteration which will trigger the loading of a new block.
+ */
+ if (pWav->msadpcm.cachedFrameCount == 0) {
+ if (pWav->msadpcm.bytesRemainingInBlock == 0) {
+ continue;
+ } else {
+ static drwav_int32 adaptationTable[] = {
+ 230, 230, 230, 230, 307, 409, 512, 614,
+ 768, 614, 512, 409, 307, 230, 230, 230
+ };
+ static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
+ static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
+
+ drwav_uint8 nibbles;
+ drwav_int32 nibble0;
+ drwav_int32 nibble1;
+
+ if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
+ return totalFramesRead;
+ }
+ pWav->msadpcm.bytesRemainingInBlock -= 1;
+
+ /* TODO: Optimize away these if statements. */
+ nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
+ nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
+
+ if (pWav->channels == 1) {
+ /* Mono. */
+ drwav_int32 newSample0;
+ drwav_int32 newSample1;
+
+ newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample0 += nibble0 * pWav->msadpcm.delta[0];
+ newSample0 = drwav_clamp(newSample0, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
+ pWav->msadpcm.prevFrames[0][1] = newSample0;
+
+
+ newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample1 += nibble1 * pWav->msadpcm.delta[0];
+ newSample1 = drwav_clamp(newSample1, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
+ pWav->msadpcm.prevFrames[0][1] = newSample1;
+
+
+ pWav->msadpcm.cachedFrames[2] = newSample0;
+ pWav->msadpcm.cachedFrames[3] = newSample1;
+ pWav->msadpcm.cachedFrameCount = 2;
+ } else {
+ /* Stereo. */
+ drwav_int32 newSample0;
+ drwav_int32 newSample1;
+
+ /* Left. */
+ newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
+ newSample0 += nibble0 * pWav->msadpcm.delta[0];
+ newSample0 = drwav_clamp(newSample0, -32768, 32767);
+
+ pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
+ if (pWav->msadpcm.delta[0] < 16) {
+ pWav->msadpcm.delta[0] = 16;
+ }
+
+ pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
+ pWav->msadpcm.prevFrames[0][1] = newSample0;
+
+
+ /* Right. */
+ newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
+ newSample1 += nibble1 * pWav->msadpcm.delta[1];
+ newSample1 = drwav_clamp(newSample1, -32768, 32767);
+
+ pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
+ if (pWav->msadpcm.delta[1] < 16) {
+ pWav->msadpcm.delta[1] = 16;
+ }
+
+ pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
+ pWav->msadpcm.prevFrames[1][1] = newSample1;
+
+ pWav->msadpcm.cachedFrames[2] = newSample0;
+ pWav->msadpcm.cachedFrames[3] = newSample1;
+ pWav->msadpcm.cachedFrameCount = 1;
+ }
+ }
+ }
+ }
+
+ return totalFramesRead;
+}
+
+
+drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalFramesRead = 0;
+
+ DRWAV_ASSERT(pWav != NULL);
+ DRWAV_ASSERT(framesToRead > 0);
+ DRWAV_ASSERT(pBufferOut != NULL);
+
+ /* TODO: Lots of room for optimization here. */
+
+ while (framesToRead > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
+ /* If there are no cached samples we need to load a new block. */
+ if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
+ if (pWav->channels == 1) {
+ /* Mono. */
+ drwav_uint8 header[4];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalFramesRead;
+ }
+ pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+ pWav->ima.stepIndex[0] = header[2];
+ pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
+ pWav->ima.cachedFrameCount = 1;
+ } else {
+ /* Stereo. */
+ drwav_uint8 header[8];
+ if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
+ return totalFramesRead;
+ }
+ pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
+
+ pWav->ima.predictor[0] = drwav__bytes_to_s16(header + 0);
+ pWav->ima.stepIndex[0] = header[2];
+ pWav->ima.predictor[1] = drwav__bytes_to_s16(header + 4);
+ pWav->ima.stepIndex[1] = header[6];
+
+ pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
+ pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
+ pWav->ima.cachedFrameCount = 1;
+ }
+ }
+
+ /* Output anything that's cached. */
+ while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->compressed.iCurrentPCMFrame < pWav->totalPCMFrameCount) {
+ drwav_uint32 iSample;
+ for (iSample = 0; iSample < pWav->channels; iSample += 1) {
+ pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
+ }
+
+ pBufferOut += pWav->channels;
+ framesToRead -= 1;
+ totalFramesRead += 1;
+ pWav->compressed.iCurrentPCMFrame += 1;
+ pWav->ima.cachedFrameCount -= 1;
+ }
+
+ if (framesToRead == 0) {
+ return totalFramesRead;
+ }
+
+ /*
+ If there's nothing left in the cache, just go ahead and load more. If there's nothing left to load in the current block we just continue to the next
+ loop iteration which will trigger the loading of a new block.
+ */
+ if (pWav->ima.cachedFrameCount == 0) {
+ if (pWav->ima.bytesRemainingInBlock == 0) {
+ continue;
+ } else {
+ static drwav_int32 indexTable[16] = {
+ -1, -1, -1, -1, 2, 4, 6, 8,
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+
+ static drwav_int32 stepTable[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
+ 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
+ 50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
+ 130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
+ 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
+ 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
+ 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
+ };
+
+ drwav_uint32 iChannel;
+
+ /*
+ From what I can tell with stereo streams, it looks like every 4 bytes (8 samples) is for one channel. So it goes 4 bytes for the
+ left channel, 4 bytes for the right channel.
+ */
+ pWav->ima.cachedFrameCount = 8;
+ for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
+ drwav_uint32 iByte;
+ drwav_uint8 nibbles[4];
+ if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
+ return totalFramesRead;
+ }
+ pWav->ima.bytesRemainingInBlock -= 4;
+
+ for (iByte = 0; iByte < 4; ++iByte) {
+ drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
+ drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
+
+ drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
+ drwav_int32 predictor = pWav->ima.predictor[iChannel];
+
+ drwav_int32 diff = step >> 3;
+ if (nibble0 & 1) diff += step >> 2;
+ if (nibble0 & 2) diff += step >> 1;
+ if (nibble0 & 4) diff += step;
+ if (nibble0 & 8) diff = -diff;
+
+ predictor = drwav_clamp(predictor + diff, -32768, 32767);
+ pWav->ima.predictor[iChannel] = predictor;
+ pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1);
+ pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
+
+
+ step = stepTable[pWav->ima.stepIndex[iChannel]];
+ predictor = pWav->ima.predictor[iChannel];
+
+ diff = step >> 3;
+ if (nibble1 & 1) diff += step >> 2;
+ if (nibble1 & 2) diff += step >> 1;
+ if (nibble1 & 4) diff += step;
+ if (nibble1 & 8) diff = -diff;
+
+ predictor = drwav_clamp(predictor + diff, -32768, 32767);
+ pWav->ima.predictor[iChannel] = predictor;
+ pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1);
+ pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
+ }
+ }
+ }
+ }
+ }
+
+ return totalFramesRead;
+}
+
+
+#ifndef DR_WAV_NO_CONVERSION_API
+static unsigned short g_drwavAlawTable[256] = {
+ 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
+ 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
+ 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
+ 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
+ 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
+ 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
+ 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
+ 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
+ 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
+ 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
+ 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
+ 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
+ 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
+ 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
+ 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
+ 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
+};
+
+static unsigned short g_drwavMulawTable[256] = {
+ 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
+ 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
+ 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
+ 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
+ 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
+ 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
+ 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
+ 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
+ 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
+ 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
+ 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
+ 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
+ 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
+ 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
+ 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
+ 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
+};
+
+static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn)
+{
+ return (short)g_drwavAlawTable[sampleIn];
+}
+
+static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn)
+{
+ return (short)g_drwavMulawTable[sampleIn];
+}
+
+
+
+static void drwav__pcm_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
+{
+ unsigned int i;
+
+ /* Special case for 8-bit sample data because it's treated as unsigned. */
+ if (bytesPerSample == 1) {
+ drwav_u8_to_s16(pOut, pIn, totalSampleCount);
+ return;
+ }
+
+
+ /* Slightly more optimal implementation for common formats. */
+ if (bytesPerSample == 2) {
+ for (i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int16*)pIn)[i];
+ }
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_s16(pOut, pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount);
+ return;
+ }
+
+
+ /* Anything more than 64 bits per sample is not supported. */
+ if (bytesPerSample > 8) {
+ DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ /* Generic, slow converter. */
+ for (i = 0; i < totalSampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (drwav_int16)((drwav_int64)sample >> 48);
+ }
+}
+
+static void drwav__ieee_to_s16(drwav_int16* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
+ return;
+ } else {
+ /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
+ DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint32 bytesPerFrame;
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ /* Fast path. */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) {
+ return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
+ }
+
+ bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) {
+ framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels;
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
+ if (!drwav__is_little_endian()) {
+ drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
+ }
+
+ return framesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut)
+{
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
+ if (drwav__is_little_endian()) {
+ drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
+ }
+
+ return framesRead;
+}
+
+
+void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ int r;
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ int x = pIn[i];
+ r = x << 8;
+ r = r - 32768;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ int r;
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ int x = ((int)(((unsigned int)(((const unsigned char*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const unsigned char*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const unsigned char*)pIn)[i*3+2])) << 24)) >> 8;
+ r = x >> 8;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+ int r;
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ int x = pIn[i];
+ r = x >> 16;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount)
+{
+ int r;
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ float x = pIn[i];
+ float c;
+ c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+ c = c + 1;
+ r = (int)(c * 32767.5f);
+ r = r - 32768;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount)
+{
+ int r;
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ double x = pIn[i];
+ double c;
+ c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
+ c = c + 1;
+ r = (int)(c * 32767.5);
+ r = r - 32768;
+ pOut[i] = (short)r;
+ }
+}
+
+void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ pOut[i] = drwav__alaw_to_s16(pIn[i]);
+ }
+}
+
+void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+ for (i = 0; i < sampleCount; ++i) {
+ pOut[i] = drwav__mulaw_to_s16(pIn[i]);
+ }
+}
+
+
+
+static void drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned int bytesPerSample)
+{
+ unsigned int i;
+
+ /* Special case for 8-bit sample data because it's treated as unsigned. */
+ if (bytesPerSample == 1) {
+ drwav_u8_to_f32(pOut, pIn, sampleCount);
+ return;
+ }
+
+ /* Slightly more optimal implementation for common formats. */
+ if (bytesPerSample == 2) {
+ drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount);
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_f32(pOut, pIn, sampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount);
+ return;
+ }
+
+
+ /* Anything more than 64 bits per sample is not supported. */
+ if (bytesPerSample > 8) {
+ DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ /* Generic, slow converter. */
+ for (i = 0; i < sampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0);
+ }
+}
+
+static void drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned int bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ unsigned int i;
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((const float*)pIn)[i];
+ }
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
+ return;
+ } else {
+ /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
+ DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+
+drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)framesRead*pWav->channels, bytesPerFrame/pWav->channels);
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
+ drwav_uint64 totalFramesRead = 0;
+ drwav_int16 samples16[2048];
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32__ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
+ drwav_uint64 totalFramesRead = 0;
+ drwav_int16 samples16[2048];
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+ drwav_uint32 bytesPerFrame;
+
+ /* Fast path. */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
+ return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
+ }
+
+ bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (bytesPerFrame > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) {
+ framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels;
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_pcm_frames_f32__msadpcm(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_pcm_frames_f32__ima(pWav, framesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
+ if (!drwav__is_little_endian()) {
+ drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
+ }
+
+ return framesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut)
+{
+ drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
+ if (drwav__is_little_endian()) {
+ drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
+ }
+
+ return framesRead;
+}
+
+
+void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+#ifdef DR_WAV_LIBSNDFILE_COMPAT
+ /*
+ It appears libsndfile uses slightly different logic for the u8 -> f32 conversion to dr_wav, which in my opinion is incorrect. It appears
+ libsndfile performs the conversion something like "f32 = (u8 / 256) * 2 - 1", however I think it should be "f32 = (u8 / 255) * 2 - 1" (note
+ the divisor of 256 vs 255). I use libsndfile as a benchmark for testing, so I'm therefore leaving this block here just for my automated
+ correctness testing. This is disabled by default.
+ */
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = (pIn[i] / 256.0f) * 2 - 1;
+ }
+#else
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = (pIn[i] / 255.0f) * 2 - 1;
+ }
+#endif
+}
+
+void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = pIn[i] / 32768.0f;
+ }
+}
+
+void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ unsigned int s0 = pIn[i*3 + 0];
+ unsigned int s1 = pIn[i*3 + 1];
+ unsigned int s2 = pIn[i*3 + 2];
+
+ int sample32 = (int)((s0 << 8) | (s1 << 16) | (s2 << 24));
+ *pOut++ = (float)(sample32 / 2147483648.0);
+ }
+}
+
+void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount)
+{
+ size_t i;
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = (float)(pIn[i] / 2147483648.0);
+ }
+}
+
+void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = (float)pIn[i];
+ }
+}
+
+void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f;
+ }
+}
+
+void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f;
+ }
+}
+
+
+
+static void drwav__pcm_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
+{
+ unsigned int i;
+
+ /* Special case for 8-bit sample data because it's treated as unsigned. */
+ if (bytesPerSample == 1) {
+ drwav_u8_to_s32(pOut, pIn, totalSampleCount);
+ return;
+ }
+
+ /* Slightly more optimal implementation for common formats. */
+ if (bytesPerSample == 2) {
+ drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 3) {
+ drwav_s24_to_s32(pOut, pIn, totalSampleCount);
+ return;
+ }
+ if (bytesPerSample == 4) {
+ for (i = 0; i < totalSampleCount; ++i) {
+ *pOut++ = ((const drwav_int32*)pIn)[i];
+ }
+ return;
+ }
+
+
+ /* Anything more than 64 bits per sample is not supported. */
+ if (bytesPerSample > 8) {
+ DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+
+
+ /* Generic, slow converter. */
+ for (i = 0; i < totalSampleCount; ++i) {
+ drwav_uint64 sample = 0;
+ unsigned int shift = (8 - bytesPerSample) * 8;
+
+ unsigned int j;
+ for (j = 0; j < bytesPerSample && j < 8; j += 1) {
+ sample |= (drwav_uint64)(pIn[j]) << shift;
+ shift += 8;
+ }
+
+ pIn += j;
+ *pOut++ = (drwav_int32)((drwav_int64)sample >> 32);
+ }
+}
+
+static void drwav__ieee_to_s32(drwav_int32* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
+{
+ if (bytesPerSample == 4) {
+ drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
+ return;
+ } else if (bytesPerSample == 8) {
+ drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
+ return;
+ } else {
+ /* Only supporting 32- and 64-bit float. Output silence in all other cases. Contributions welcome for 16-bit float. */
+ DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
+ return;
+ }
+}
+
+
+drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+ drwav_uint32 bytesPerFrame;
+
+ /* Fast path. */
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
+ return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut);
+ }
+
+ bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
+ drwav_uint64 totalFramesRead = 0;
+ drwav_int16 samples16[2048];
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ /*
+ We're just going to borrow the implementation from the drwav_read_s16() since IMA-ADPCM is a little bit more complicated than other formats and I don't
+ want to duplicate that code.
+ */
+ drwav_uint64 totalFramesRead = 0;
+ drwav_int16 samples16[2048];
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels), samples16);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); /* <-- Safe cast because we're clamping to 2048. */
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels), bytesPerFrame/pWav->channels);
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ drwav_uint64 totalFramesRead;
+ unsigned char sampleData[4096];
+
+ drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav);
+ if (bytesPerFrame == 0) {
+ return 0;
+ }
+
+ totalFramesRead = 0;
+
+ while (framesToRead > 0) {
+ drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame), sampleData);
+ if (framesRead == 0) {
+ break;
+ }
+
+ drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)(framesRead*pWav->channels));
+
+ pBufferOut += framesRead*pWav->channels;
+ framesToRead -= framesRead;
+ totalFramesRead += framesRead;
+ }
+
+ return totalFramesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ if (pWav == NULL || framesToRead == 0 || pBufferOut == NULL) {
+ return 0;
+ }
+
+ /* Don't try to read more samples than can potentially fit in the output buffer. */
+ if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) {
+ framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels;
+ }
+
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) {
+ return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) {
+ return drwav_read_pcm_frames_s32__msadpcm(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) {
+ return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) {
+ return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) {
+ return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
+ }
+
+ if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) {
+ return drwav_read_pcm_frames_s32__ima(pWav, framesToRead, pBufferOut);
+ }
+
+ return 0;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
+ if (!drwav__is_little_endian()) {
+ drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
+ }
+
+ return framesRead;
+}
+
+drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut)
+{
+ drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
+ if (drwav__is_little_endian()) {
+ drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
+ }
+
+ return framesRead;
+}
+
+
+void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((int)pIn[i] - 128) << 24;
+ }
+}
+
+void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = pIn[i] << 16;
+ }
+}
+
+void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ unsigned int s0 = pIn[i*3 + 0];
+ unsigned int s1 = pIn[i*3 + 1];
+ unsigned int s2 = pIn[i*3 + 2];
+
+ drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
+ *pOut++ = sample32;
+ }
+}
+
+void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+ }
+}
+
+void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]);
+ }
+}
+
+void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i = 0; i < sampleCount; ++i) {
+ *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16;
+ }
+}
+
+void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount)
+{
+ size_t i;
+
+ if (pOut == NULL || pIn == NULL) {
+ return;
+ }
+
+ for (i= 0; i < sampleCount; ++i) {
+ *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16;
+ }
+}
+
+
+
+drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
+{
+ drwav_uint64 sampleDataSize;
+ drwav_int16* pSampleData;
+ drwav_uint64 framesRead;
+
+ DRWAV_ASSERT(pWav != NULL);
+
+ sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; /* File's too big. */
+ }
+
+ pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; /* Failed to allocate memory. */
+ }
+
+ framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
+ if (framesRead != pWav->totalPCMFrameCount) {
+ drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
+ drwav_uninit(pWav);
+ return NULL; /* There was an error reading the samples. */
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) {
+ *sampleRate = pWav->sampleRate;
+ }
+ if (channels) {
+ *channels = pWav->channels;
+ }
+ if (totalFrameCount) {
+ *totalFrameCount = pWav->totalPCMFrameCount;
+ }
+
+ return pSampleData;
+}
+
+float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
+{
+ drwav_uint64 sampleDataSize;
+ float* pSampleData;
+ drwav_uint64 framesRead;
+
+ DRWAV_ASSERT(pWav != NULL);
+
+ sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; /* File's too big. */
+ }
+
+ pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; /* Failed to allocate memory. */
+ }
+
+ framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
+ if (framesRead != pWav->totalPCMFrameCount) {
+ drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
+ drwav_uninit(pWav);
+ return NULL; /* There was an error reading the samples. */
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) {
+ *sampleRate = pWav->sampleRate;
+ }
+ if (channels) {
+ *channels = pWav->channels;
+ }
+ if (totalFrameCount) {
+ *totalFrameCount = pWav->totalPCMFrameCount;
+ }
+
+ return pSampleData;
+}
+
+drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount)
+{
+ drwav_uint64 sampleDataSize;
+ drwav_int32* pSampleData;
+ drwav_uint64 framesRead;
+
+ DRWAV_ASSERT(pWav != NULL);
+
+ sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32);
+ if (sampleDataSize > DRWAV_SIZE_MAX) {
+ drwav_uninit(pWav);
+ return NULL; /* File's too big. */
+ }
+
+ pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); /* <-- Safe cast due to the check above. */
+ if (pSampleData == NULL) {
+ drwav_uninit(pWav);
+ return NULL; /* Failed to allocate memory. */
+ }
+
+ framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
+ if (framesRead != pWav->totalPCMFrameCount) {
+ drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
+ drwav_uninit(pWav);
+ return NULL; /* There was an error reading the samples. */
+ }
+
+ drwav_uninit(pWav);
+
+ if (sampleRate) {
+ *sampleRate = pWav->sampleRate;
+ }
+ if (channels) {
+ *channels = pWav->channels;
+ }
+ if (totalFrameCount) {
+ *totalFrameCount = pWav->totalPCMFrameCount;
+ }
+
+ return pSampleData;
+}
+
+
+
+drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+#ifndef DR_WAV_NO_STDIO
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+
+drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+#endif
+
+drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+
+drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ drwav wav;
+
+ if (channelsOut) {
+ *channelsOut = 0;
+ }
+ if (sampleRateOut) {
+ *sampleRateOut = 0;
+ }
+ if (totalFrameCountOut) {
+ *totalFrameCountOut = 0;
+ }
+
+ if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
+ return NULL;
+ }
+
+ return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
+}
+#endif /* DR_WAV_NO_CONVERSION_API */
+
+
+void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks)
+{
+ if (pAllocationCallbacks != NULL) {
+ drwav__free_from_callbacks(p, pAllocationCallbacks);
+ } else {
+ drwav__free_default(p, NULL);
+ }
+}
+
+#endif /* DR_WAV_IMPLEMENTATION */
+
+/*
+REVISION HISTORY
+================
+v0.11.1 - 2019-10-07
+ - Internal code clean up.
+
+v0.11.0 - 2019-10-06
+ - API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation
+ routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:
+ - drwav_init()
+ - drwav_init_ex()
+ - drwav_init_file()
+ - drwav_init_file_ex()
+ - drwav_init_file_w()
+ - drwav_init_file_w_ex()
+ - drwav_init_memory()
+ - drwav_init_memory_ex()
+ - drwav_init_write()
+ - drwav_init_write_sequential()
+ - drwav_init_write_sequential_pcm_frames()
+ - drwav_init_file_write()
+ - drwav_init_file_write_sequential()
+ - drwav_init_file_write_sequential_pcm_frames()
+ - drwav_init_file_write_w()
+ - drwav_init_file_write_sequential_w()
+ - drwav_init_file_write_sequential_pcm_frames_w()
+ - drwav_init_memory_write()
+ - drwav_init_memory_write_sequential()
+ - drwav_init_memory_write_sequential_pcm_frames()
+ - drwav_open_and_read_pcm_frames_s16()
+ - drwav_open_and_read_pcm_frames_f32()
+ - drwav_open_and_read_pcm_frames_s32()
+ - drwav_open_file_and_read_pcm_frames_s16()
+ - drwav_open_file_and_read_pcm_frames_f32()
+ - drwav_open_file_and_read_pcm_frames_s32()
+ - drwav_open_file_and_read_pcm_frames_s16_w()
+ - drwav_open_file_and_read_pcm_frames_f32_w()
+ - drwav_open_file_and_read_pcm_frames_s32_w()
+ - drwav_open_memory_and_read_pcm_frames_s16()
+ - drwav_open_memory_and_read_pcm_frames_f32()
+ - drwav_open_memory_and_read_pcm_frames_s32()
+ Set this extra parameter to NULL to use defaults which is the same as the previous behaviour. Setting this NULL will use
+ DRWAV_MALLOC, DRWAV_REALLOC and DRWAV_FREE.
+ - Add support for reading and writing PCM frames in an explicit endianness. New APIs:
+ - drwav_read_pcm_frames_le()
+ - drwav_read_pcm_frames_be()
+ - drwav_read_pcm_frames_s16le()
+ - drwav_read_pcm_frames_s16be()
+ - drwav_read_pcm_frames_f32le()
+ - drwav_read_pcm_frames_f32be()
+ - drwav_read_pcm_frames_s32le()
+ - drwav_read_pcm_frames_s32be()
+ - drwav_write_pcm_frames_le()
+ - drwav_write_pcm_frames_be()
+ - Remove deprecated APIs.
+ - API CHANGE: The following APIs now return native-endian data. Previously they returned little-endian data.
+ - drwav_read_pcm_frames()
+ - drwav_read_pcm_frames_s16()
+ - drwav_read_pcm_frames_s32()
+ - drwav_read_pcm_frames_f32()
+ - drwav_open_and_read_pcm_frames_s16()
+ - drwav_open_and_read_pcm_frames_s32()
+ - drwav_open_and_read_pcm_frames_f32()
+ - drwav_open_file_and_read_pcm_frames_s16()
+ - drwav_open_file_and_read_pcm_frames_s32()
+ - drwav_open_file_and_read_pcm_frames_f32()
+ - drwav_open_file_and_read_pcm_frames_s16_w()
+ - drwav_open_file_and_read_pcm_frames_s32_w()
+ - drwav_open_file_and_read_pcm_frames_f32_w()
+ - drwav_open_memory_and_read_pcm_frames_s16()
+ - drwav_open_memory_and_read_pcm_frames_s32()
+ - drwav_open_memory_and_read_pcm_frames_f32()
+
+v0.10.1 - 2019-08-31
+ - Correctly handle partial trailing ADPCM blocks.
+
+v0.10.0 - 2019-08-04
+ - Remove deprecated APIs.
+ - Add wchar_t variants for file loading APIs:
+ drwav_init_file_w()
+ drwav_init_file_ex_w()
+ drwav_init_file_write_w()
+ drwav_init_file_write_sequential_w()
+ - Add drwav_target_write_size_bytes() which calculates the total size in bytes of a WAV file given a format and sample count.
+ - Add APIs for specifying the PCM frame count instead of the sample count when opening in sequential write mode:
+ drwav_init_write_sequential_pcm_frames()
+ drwav_init_file_write_sequential_pcm_frames()
+ drwav_init_file_write_sequential_pcm_frames_w()
+ drwav_init_memory_write_sequential_pcm_frames()
+ - Deprecate drwav_open*() and drwav_close():
+ drwav_open()
+ drwav_open_ex()
+ drwav_open_write()
+ drwav_open_write_sequential()
+ drwav_open_file()
+ drwav_open_file_ex()
+ drwav_open_file_write()
+ drwav_open_file_write_sequential()
+ drwav_open_memory()
+ drwav_open_memory_ex()
+ drwav_open_memory_write()
+ drwav_open_memory_write_sequential()
+ drwav_close()
+ - Minor documentation updates.
+
+v0.9.2 - 2019-05-21
+ - Fix warnings.
+
+v0.9.1 - 2019-05-05
+ - Add support for C89.
+ - Change license to choice of public domain or MIT-0.
+
+v0.9.0 - 2018-12-16
+ - API CHANGE: Add new reading APIs for reading by PCM frames instead of samples. Old APIs have been deprecated and
+ will be removed in v0.10.0. Deprecated APIs and their replacements:
+ drwav_read() -> drwav_read_pcm_frames()
+ drwav_read_s16() -> drwav_read_pcm_frames_s16()
+ drwav_read_f32() -> drwav_read_pcm_frames_f32()
+ drwav_read_s32() -> drwav_read_pcm_frames_s32()
+ drwav_seek_to_sample() -> drwav_seek_to_pcm_frame()
+ drwav_write() -> drwav_write_pcm_frames()
+ drwav_open_and_read_s16() -> drwav_open_and_read_pcm_frames_s16()
+ drwav_open_and_read_f32() -> drwav_open_and_read_pcm_frames_f32()
+ drwav_open_and_read_s32() -> drwav_open_and_read_pcm_frames_s32()
+ drwav_open_file_and_read_s16() -> drwav_open_file_and_read_pcm_frames_s16()
+ drwav_open_file_and_read_f32() -> drwav_open_file_and_read_pcm_frames_f32()
+ drwav_open_file_and_read_s32() -> drwav_open_file_and_read_pcm_frames_s32()
+ drwav_open_memory_and_read_s16() -> drwav_open_memory_and_read_pcm_frames_s16()
+ drwav_open_memory_and_read_f32() -> drwav_open_memory_and_read_pcm_frames_f32()
+ drwav_open_memory_and_read_s32() -> drwav_open_memory_and_read_pcm_frames_s32()
+ drwav::totalSampleCount -> drwav::totalPCMFrameCount
+ - API CHANGE: Rename drwav_open_and_read_file_*() to drwav_open_file_and_read_*().
+ - API CHANGE: Rename drwav_open_and_read_memory_*() to drwav_open_memory_and_read_*().
+ - Add built-in support for smpl chunks.
+ - Add support for firing a callback for each chunk in the file at initialization time.
+ - This is enabled through the drwav_init_ex(), etc. family of APIs.
+ - Handle invalid FMT chunks more robustly.
+
+v0.8.5 - 2018-09-11
+ - Const correctness.
+ - Fix a potential stack overflow.
+
+v0.8.4 - 2018-08-07
+ - Improve 64-bit detection.
+
+v0.8.3 - 2018-08-05
+ - Fix C++ build on older versions of GCC.
+
+v0.8.2 - 2018-08-02
+ - Fix some big-endian bugs.
+
+v0.8.1 - 2018-06-29
+ - Add support for sequential writing APIs.
+ - Disable seeking in write mode.
+ - Fix bugs with Wave64.
+ - Fix typos.
+
+v0.8 - 2018-04-27
+ - Bug fix.
+ - Start using major.minor.revision versioning.
+
+v0.7f - 2018-02-05
+ - Restrict ADPCM formats to a maximum of 2 channels.
+
+v0.7e - 2018-02-02
+ - Fix a crash.
+
+v0.7d - 2018-02-01
+ - Fix a crash.
+
+v0.7c - 2018-02-01
+ - Set drwav.bytesPerSample to 0 for all compressed formats.
+ - Fix a crash when reading 16-bit floating point WAV files. In this case dr_wav will output silence for
+ all format conversion reading APIs (*_s16, *_s32, *_f32 APIs).
+ - Fix some divide-by-zero errors.
+
+v0.7b - 2018-01-22
+ - Fix errors with seeking of compressed formats.
+ - Fix compilation error when DR_WAV_NO_CONVERSION_API
+
+v0.7a - 2017-11-17
+ - Fix some GCC warnings.
+
+v0.7 - 2017-11-04
+ - Add writing APIs.
+
+v0.6 - 2017-08-16
+ - API CHANGE: Rename dr_* types to drwav_*.
+ - Add support for custom implementations of malloc(), realloc(), etc.
+ - Add support for Microsoft ADPCM.
+ - Add support for IMA ADPCM (DVI, format code 0x11).
+ - Optimizations to drwav_read_s16().
+ - Bug fixes.
+
+v0.5g - 2017-07-16
+ - Change underlying type for booleans to unsigned.
+
+v0.5f - 2017-04-04
+ - Fix a minor bug with drwav_open_and_read_s16() and family.
+
+v0.5e - 2016-12-29
+ - Added support for reading samples as signed 16-bit integers. Use the _s16() family of APIs for this.
+ - Minor fixes to documentation.
+
+v0.5d - 2016-12-28
+ - Use drwav_int* and drwav_uint* sized types to improve compiler support.
+
+v0.5c - 2016-11-11
+ - Properly handle JUNK chunks that come before the FMT chunk.
+
+v0.5b - 2016-10-23
+ - A minor change to drwav_bool8 and drwav_bool32 types.
+
+v0.5a - 2016-10-11
+ - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering.
+ - Improve A-law and mu-law efficiency.
+
+v0.5 - 2016-09-29
+ - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to
+ keep it consistent with dr_audio and dr_flac.
+
+v0.4b - 2016-09-18
+ - Fixed a typo in documentation.
+
+v0.4a - 2016-09-18
+ - Fixed a typo.
+ - Change date format to ISO 8601 (YYYY-MM-DD)
+
+v0.4 - 2016-07-13
+ - API CHANGE. Make onSeek consistent with dr_flac.
+ - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac.
+ - Added support for Sony Wave64.
+
+v0.3a - 2016-05-28
+ - API CHANGE. Return drwav_bool32 instead of int in onSeek callback.
+ - Fixed a memory leak.
+
+v0.3 - 2016-05-22
+ - Lots of API changes for consistency.
+
+v0.2a - 2016-05-16
+ - Fixed Linux/GCC build.
+
+v0.2 - 2016-05-11
+ - Added support for reading data as signed 32-bit PCM for consistency with dr_flac.
+
+v0.1a - 2016-05-07
+ - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize.
+
+v0.1 - 2016-05-04
+ - Initial versioned release.
+*/
+
+/*
+This software is available as a choice of the following licenses. Choose
+whichever you prefer.
+
+===============================================================================
+ALTERNATIVE 1 - 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.
+
+For more information, please refer to <http://unlicense.org/>
+
+===============================================================================
+ALTERNATIVE 2 - MIT No Attribution
+===============================================================================
+Copyright 2018 David Reid
+
+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.
+
+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.
+*/
diff --git a/src/libs/decoders/flac.c b/src/libs/decoders/flac.c
new file mode 100644
index 000000000..158fcfc82
--- /dev/null
+++ b/src/libs/decoders/flac.c
@@ -0,0 +1,186 @@
+/*
+ * DOSBox FLAC decoder is maintained by Kevin R. Croft (krcroft@gmail.com)
+ * This decoder makes use of the excellent dr_flac library by David Reid (mackron@gmail.com)
+ *
+ * Source links
+ * - dr_libs: https://github.com/mackron/dr_libs (source)
+ * - dr_flac: http://mackron.github.io/dr_flac.html (website)
+ *
+ * The upstream SDL2 Sound 1.9.x FLAC decoder is written and copyright by Ryan C. Gordon. (icculus@icculus.org)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file is part of the SDL Sound Library.
+ *
+ * This SDL_sound Ogg Opus decoder backend is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This SDL_sound Ogg Opus decoder backend is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the SDL Sound Library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "SDL_sound.h"
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h"
+
+#define DR_FLAC_IMPLEMENTATION
+// #define DR_FLAC_NO_SIMD 1 /* temporary work-around for https://github.com/mackron/dr_libs/issues/63 */
+#define DR_FLAC_NO_STDIO 1
+#define DR_FLAC_NO_WIN32_IO 1
+#define DR_FLAC_NO_CRC 1
+#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;
+
+ while (retval < bytesToRead)
+ {
+ const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead);
+ if (rc == 0)
+ {
+ sample->flags |= SOUND_SAMPLEFLAG_EOF;
+ break;
+ } /* 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, NULL);
+
+ 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_S16SYS; /* returns native byte-order based on architecture */
+
+ const Uint64 frames = (Uint64) dr->totalPCMFrameCount;
+ if (frames == 0)
+ internal->total_time = -1;
+ else
+ {
+ const Uint32 rate = (Uint32) dr->sampleRate;
+ internal->total_time = (frames / rate) * 1000;
+ internal->total_time += ((frames % rate) * 1000) / rate;
+ } /* 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_pcm_frames_s16(dr,
+ internal->buffer_size / (dr->channels * sizeof(drflac_int16)),
+ (drflac_int16 *) internal->buffer);
+ return rc * dr->channels * sizeof (drflac_int16);
+} /* 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_pcm_frame(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));
+ return (drflac_seek_to_pcm_frame(dr, frame_offset) == 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 <icculus@icculus.org>",
+ "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 */
+};
+
+/* end of flac.c ... */
diff --git a/src/libs/decoders/internal/Makefile b/src/libs/decoders/internal/Makefile
new file mode 100644
index 000000000..d9beab00a
--- /dev/null
+++ b/src/libs/decoders/internal/Makefile
@@ -0,0 +1,210 @@
+##
+# Fetch the latest dependencies from upstream
+# ===========================================
+OGG_ARCHIVE = ogg-master.tar.gz
+OGG_URL = https://github.com/xiph/ogg/archive/master.tar.gz
+
+OPUS_ARCHIVE = opus-master.tar.gz
+OPUS_URL = https://github.com/xiph/opus/archive/master.tar.gz
+
+OPUSFILE_ARCHIVE = opusfile-master.tar.gz
+OPUSFILE_URL = https://github.com/xiph/opusfile/archive/master.tar.gz
+
+# We use speex's pkg.m4 to work around a bug in opusfile's autotools config
+# that still depends on pkgconfig even if DEPS_ env flags are present.
+# Mingw 1.0 (Windows) does not provide pkgconfig, so we need to avoid it.
+OPUSFILE_PKG_URL = https://raw.githubusercontent.com/xiph/speex/master/m4/pkg.m4
+
+SPEEXDSP_ARCHIVE = speexdsp-master.tar.gz
+SPEEXDSP_URL = https://github.com/xiph/speexdsp/archive/master.tar.gz
+
+##
+# The audio-codecs rely on accurate floating-point calculations
+# and will fail to build (Opus) if they detect too agressive optimizations
+# such as Ofast or ffast-math; therefore we strip just these flags
+# and step-down -Ofast to -O3 as an acceptable fallback.
+#
+CFLAGS:= $(subst -Ofast,-O3,$(CFLAGS))
+CFLAGS:= $(filter-out -ffast-math,$(CFLAGS))
+CFLAGS:= $(filter-out -funsafe-math-optimizations,$(CFLAGS))
+
+CXXFLAGS:= $(subst -Ofast,-O3,$(CXXFLAGS))
+CXXFLAGS:= $(filter-out -ffast-math,$(CXXFLAGS))
+CXXFLAGS:= $(filter-out -funsafe-math-optimizations,$(CXXFLAGS))
+
+LDFLAGS:= $(subst -Ofast,-O3,$(LDLAGS))
+LDFLAGS:= $(filter-out -ffast-math,$(LDFLAGS))
+LDFLAGS:= $(filter-out -funsafe-math-optimizations,$(LDFLAGS))
+
+##
+# Common commands and arguments
+# =============================
+THREADS = $(shell nproc || echo 4)
+CURL = curl -s -L
+EXTRACT = tar --strip 1 -zxof
+
+##
+# Everything-targets
+# ==================
+all: ogg speexdsp opus opusfile
+clean: ogg/clean speexdsp/clean opus/clean opusfile/clean
+dist-clean:
+ rm -rf ogg speexdsp opus opusfile include lib share pkg.m4 *.gz
+
+##
+# Ogg Library
+# ===========
+ogg: lib/libogg.a
+
+$(OGG_ARCHIVE):
+ $(CURL) "$(OGG_URL)" -o "$(OGG_ARCHIVE)"
+
+ogg/autogen.sh: $(OGG_ARCHIVE)
+ test -f $@ \
+ || ( mkdir -p ogg \
+ && $(EXTRACT) $(OGG_ARCHIVE) -C ogg )
+
+ogg/configure: ogg/autogen.sh
+ cd ogg \
+ && mkdir -p m4 \
+ && ./autogen.sh
+
+ogg/Makefile: ogg/configure
+ cd ogg \
+ && ./configure --disable-shared
+
+lib/libogg.a: ogg/Makefile
+ cd ogg \
+ && $(MAKE) -j$(THREADS) \
+ && mkdir -p ../include/ogg ../lib \
+ && cp -v include/ogg/*.h ../include/ogg \
+ && cp -v src/.libs/libogg.a ../lib
+
+ogg/clean:
+ cd ogg \
+ && $(MAKE) clean \
+ && rm -f configure Makefile
+
+##
+# Speex DSP Library
+# =================
+speexdsp: lib/libspeexdsp.a
+
+$(SPEEXDSP_ARCHIVE):
+ $(CURL) "$(SPEEXDSP_URL)" -o "$(SPEEXDSP_ARCHIVE)"
+
+speexdsp/autogen.sh: $(SPEEXDSP_ARCHIVE)
+ test -f $@ \
+ || ( mkdir -p speexdsp \
+ && $(EXTRACT) $(SPEEXDSP_ARCHIVE) -C speexdsp )
+
+speexdsp/configure: speexdsp/autogen.sh
+ cd speexdsp \
+ && mkdir -p m4 \
+ && ./autogen.sh
+
+# Note: the strange looking sed -i.bak syntax is required for OSX compatibility
+# and is still compatible on Linux and Windows-mingGW (thankfully!)
+#
+speexdsp/Makefile: speexdsp/configure
+ cd speexdsp \
+ && sed -i.bak 's/.*PKG_CHECK_MODULES.*//g' configure \
+ && ./configure --disable-shared --disable-examples --with-fft=smallft
+
+lib/libspeexdsp.a: speexdsp/Makefile
+ cd speexdsp \
+ && sed -i.bak 's/@FFT_CFLAGS@//g' libspeexdsp/Makefile \
+ && $(MAKE) -j$(THREADS) \
+ && mkdir -p ../include/speex ../lib \
+ && cp -v include/speex/*.h ../include/speex \
+ && cp -v libspeexdsp/.libs/libspeexdsp.a ../lib
+
+speexdsp/clean:
+ cd speexdsp \
+ && $(MAKE) clean \
+ && rm -f configure Makefile
+
+##
+# Opus Library
+# ============
+opus: lib/libopus.a
+
+$(OPUS_ARCHIVE):
+ $(CURL) "$(OPUS_URL)" -o "$(OPUS_ARCHIVE)"
+
+opus/autogen.sh: $(OPUS_ARCHIVE)
+ test -f $@ \
+ || ( mkdir -p opus \
+ && $(EXTRACT) $(OPUS_ARCHIVE) -C opus )
+
+opus/configure: opus/autogen.sh
+ cd opus \
+ && mkdir -p m4 \
+ && ./autogen.sh
+
+opus/Makefile: opus/configure
+ cd opus \
+ && ./configure --disable-shared --disable-extra-programs --disable-doc
+
+lib/libopus.a: opus/Makefile
+ cd opus \
+ && $(MAKE) -j$(THREADS) \
+ && mkdir -p ../include/opus ../lib \
+ && cp -v include/*.h ../include/opus \
+ && cp -v .libs/libopus.a ../lib
+
+opus/clean:
+ cd opus \
+ && $(MAKE) clean \
+ && rm -f configure Makefile
+
+##
+# Opusfile Library
+# ================
+opusfile: lib/libopusfile.a
+
+pkg.m4:
+ $(CURL) "$(OPUSFILE_PKG_URL)" -o "pkg.m4"
+
+$(OPUSFILE_ARCHIVE):
+ $(CURL) "$(OPUSFILE_URL)" -o "$(OPUSFILE_ARCHIVE)"
+
+opusfile/autogen.sh: $(OPUSFILE_ARCHIVE)
+ test -f $@ \
+ || ( mkdir -p opusfile \
+ && $(EXTRACT) $(OPUSFILE_ARCHIVE) -C opusfile )
+
+opusfile/configure: opusfile/autogen.sh pkg.m4
+ cd opusfile \
+ && mkdir -p m4 \
+ && cp ../pkg.m4 m4/ \
+ && ./autogen.sh
+
+opusfile/Makefile: opusfile/configure lib/libopus.a lib/libogg.a
+ cd opusfile && \
+ DEPS_LIBS="-L../lib -lopus -logg" \
+ DEPS_CFLAGS="-I../include -I../include/opus -I../include/ogg" \
+ ./configure --disable-shared --disable-doc --disable-http --disable-examples
+
+# We run make twice below. We hide the output of the first run because the
+# auto-tools generated Makefile launches sed and libtool without quoting the
+# current working directory, so those fail when run inside a directory structure
+# containing one or more spaces.
+#
+# Regardless of the sed/libtool issue, if all went well the resulting library will
+# exist, which is why we run make a second time. The second make pass doesn't
+# rerun the sed and libtool comands, so it acts as a safety check. If there's a
+# actual build problem with the source code then it will be caught in this
+# second make run and fail without producing a library.
+#
+lib/libopusfile.a: opusfile/Makefile
+ cd opusfile \
+ && ( $(MAKE) -j$(THREADS) > make.log 2>&1 || $(MAKE) ) \
+ && mkdir -p ../include/ ../lib \
+ && cp -v include/*.h ../include/opus \
+ && cp -v .libs/*.a ../lib
+
+opusfile/clean:
+ cd opusfile \
+ && $(MAKE) clean \
+ && rm -f configure Makefile
diff --git a/src/libs/decoders/mp3.cpp b/src/libs/decoders/mp3.cpp
new file mode 100644
index 000000000..1754b454a
--- /dev/null
+++ b/src/libs/decoders/mp3.cpp
@@ -0,0 +1,223 @@
+/**
+ * This DOSBox mp3 decooder backend is maintained by Kevin R. Croft (krcroft@gmail.com)
+ * This decoder makes use of the following single-header public libraries:
+ * - dr_mp3: http://mackron.github.io/dr_mp3.html, by David Reid
+ *
+ * The upstream SDL2 Sound 1.9.x mp3 decoder is written and copyright by Ryan C. Gordon. (icculus@icculus.org)
+ *
+ * This SDL_sound MP3 decoder backend is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This MP3 decoder backend is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the SDL Sound Library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <assert.h>
+
+#include <SDL.h> // provides: SDL_malloc, SDL_realloc, SDL_free, SDL_memcpy, and SDL_memset
+#define DR_MP3_IMPLEMENTATION
+#define DR_MP3_NO_STDIO 1
+#define DRMP3_ASSERT(x) 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))
+#include "dr_mp3.h" // provides: drmp3
+
+#include "../../../include/logging.h" // provides: LOG_MSG
+#include "mp3_seek_table.h" // provides: populate_seek_table and SDL_Sound headers
+
+#include "SDL_sound.h"
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h" // provides: Sound_SampleInternal
+
+#define MP3_FAST_SEEK_FILENAME "fastseek.lut"
+
+static size_t mp3_read(void* const pUserData, void* const pBufferOut, const size_t bytesToRead)
+{
+ Uint8* ptr = static_cast<Uint8*>(pBufferOut);
+ Sound_Sample* const sample = static_cast<Sound_Sample* const>(pUserData);
+ const Sound_SampleInternal* const internal = static_cast<const Sound_SampleInternal*>(sample->opaque);
+ SDL_RWops* rwops = internal->rw;
+ size_t retval = 0;
+
+ while (retval < bytesToRead)
+ {
+ const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead);
+ if (rc == 0)
+ {
+ sample->flags |= SOUND_SAMPLEFLAG_EOF;
+ break;
+ } /* if */
+ else
+ {
+ retval += rc;
+ ptr += rc;
+ } /* else */
+ } /* while */
+
+ return retval;
+} /* mp3_read */
+
+static drmp3_bool32 mp3_seek(void* const pUserData, const Sint32 offset, const drmp3_seek_origin origin)
+{
+ const Sint32 whence = (origin == drmp3_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR;
+ Sound_Sample* const sample = static_cast<Sound_Sample*>(pUserData);
+ Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque);
+ return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRMP3_TRUE : DRMP3_FALSE;
+} /* mp3_seek */
+
+
+static Sint32 MP3_init(void)
+{
+ return 1; /* always succeeds. */
+} /* MP3_init */
+
+
+static void MP3_quit(void)
+{
+ /* it's a no-op. */
+} /* MP3_quit */
+
+static void MP3_close(Sound_Sample* const sample)
+{
+ Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal* const>(sample->opaque);
+ mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private);
+ if (p_mp3 != NULL) {
+ if (p_mp3->p_dr != NULL) {
+ drmp3_uninit(p_mp3->p_dr);
+ SDL_free(p_mp3->p_dr);
+ }
+ // maps and vector destructors free their memory
+ SDL_free(p_mp3);
+ internal->decoder_private = NULL;
+ }
+} /* MP3_close */
+
+static Uint32 MP3_read(Sound_Sample* const sample)
+{
+ Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal* const>(sample->opaque);
+ const Sint32 channels = (int) sample->actual.channels;
+ mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private);
+
+ // setup our 32-bit input buffer
+ float in_buffer[4096];
+ const drmp3_uint16 in_buffer_frame_capacity = 4096 / channels;
+
+ // setup our 16-bit output buffer
+ drmp3_int16* out_buffer = static_cast<drmp3_int16*>(internal->buffer);
+ drmp3_uint16 remaining_frames = (internal->buffer_size / sizeof(drmp3_int16)) / channels;
+
+ // LOG_MSG("read: remaining_frames: %u", remaining_frames);
+ drmp3_uint16 total_samples_read = 0;
+ while (remaining_frames > 0) {
+ const drmp3_uint16 num_frames = (remaining_frames > in_buffer_frame_capacity) ? in_buffer_frame_capacity : remaining_frames;
+
+ // LOG_MSG("read-while: num_frames: %u", num_frames);
+ const drmp3_uint16 frames_just_read = drmp3_read_pcm_frames_f32(p_mp3->p_dr, num_frames, in_buffer);
+
+ // LOG_MSG("read-while: frames_just_read: %u", frames_just_read);
+ if (frames_just_read == 0) break; // Reached the end.
+
+ const drmp3_uint16 samples_just_read = frames_just_read * channels;
+
+ // f32 -> s16
+ drmp3dec_f32_to_s16(in_buffer, out_buffer, samples_just_read);
+
+ remaining_frames -= frames_just_read;
+ out_buffer += samples_just_read;
+ total_samples_read += samples_just_read;
+ }
+ // SNDDBG(("encoded stream offset: %d", SDL_RWtell(internal->rw) ));
+
+ return total_samples_read * sizeof(drmp3_int16);
+} /* MP3_read */
+
+static Sint32 MP3_open(Sound_Sample* const sample, const char* const ext)
+{
+ Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque);
+ Sint32 result(0); // assume failure until proven otherwise
+ mp3_t* p_mp3 = (mp3_t*) SDL_calloc(1, sizeof (mp3_t));
+ if (p_mp3 != NULL) {
+ p_mp3->p_dr = (drmp3*) SDL_calloc(1, sizeof (drmp3));
+ if (p_mp3->p_dr != NULL) {
+ result = drmp3_init(p_mp3->p_dr, mp3_read, mp3_seek, sample, NULL, NULL);
+ if (result == DRMP3_TRUE) {
+ SNDDBG(("MP3: Accepting data stream.\n"));
+ sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
+ sample->actual.channels = p_mp3->p_dr->channels;
+ sample->actual.rate = p_mp3->p_dr->sampleRate;
+ sample->actual.format = AUDIO_S16SYS; // returns native byte-order based on architecture
+ const Uint64 num_frames = populate_seek_points(internal->rw, p_mp3, MP3_FAST_SEEK_FILENAME); // status will be 0 or pcm_frame_count
+ if (num_frames != 0) {
+ const unsigned int rate = p_mp3->p_dr->sampleRate;
+ internal->total_time = (num_frames / rate) * 1000;
+ internal->total_time += (num_frames % rate) * 1000 / rate;
+ result = 1;
+ } else {
+ internal->total_time = -1;
+ LOG_MSG("MP3: populate_seek_table failed to create seek points for the stream; falling back to brute-force seeking.");
+ }
+ } else { LOG_MSG("MP3: drmp3_init(...) failed to parse and initialize the mp3 stream"); }
+ } else { LOG_MSG("MP3: failed to allocate memory for the drmp3 object"); }
+ } else { LOG_MSG("MP3: failed to allocate memory for the mp3_t object"); }
+
+ // Assign our internal decoder to the mp3 object we've just populated
+ internal->decoder_private = p_mp3;
+
+ // if anything went wrong then tear down our private structure
+ if (result == 0)
+ MP3_close(sample);
+
+ return static_cast<Sint32>(result);
+} /* MP3_open */
+
+static Sint32 MP3_rewind(Sound_Sample* const sample)
+{
+ Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque);
+ mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private);
+ return (drmp3_seek_to_start_of_stream(p_mp3->p_dr) == DRMP3_TRUE);
+} /* MP3_rewind */
+
+static Sint32 MP3_seek(Sound_Sample* const sample, const Uint32 ms)
+{
+ Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal* const>(sample->opaque);
+ mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private);
+ const float frames_per_ms = sample->actual.rate / 1000.0f;
+ const drmp3_uint64 frame_offset = frames_per_ms * ms;
+ const Sint32 result = drmp3_seek_to_pcm_frame(p_mp3->p_dr, frame_offset);
+ return (result == DRMP3_TRUE);
+} /* MP3_seek */
+
+/* dr_mp3 will play layer 1 and 2 files, too */
+static const char* extensions_mp3[] = { "MP3", "MP2", "MP1", NULL };
+
+extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3 = {
+ {
+ extensions_mp3,
+ "MPEG-1 Audio Layer I-III",
+ "Ryan C. Gordon <icculus@icculus.org>",
+ "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 */
+}; }
+/* end of SDL_sound_mp3.c ... */
diff --git a/src/libs/decoders/mp3_seek_table.cpp b/src/libs/decoders/mp3_seek_table.cpp
new file mode 100644
index 000000000..389015f93
--- /dev/null
+++ b/src/libs/decoders/mp3_seek_table.cpp
@@ -0,0 +1,339 @@
+/**
+ * DOSBox MP3 Seek Table handler, Copyright 2018 Kevin R. Croft (krcroft@gmail.com)
+ *
+ * Problem:
+ * Seeking within an MP3 file to an exact time-offset, such as is expected
+ * within DOS games, is extremely difficult because the MP3 format doesn't
+ * provide a defined relationship between the compressed data stream positions
+ * versus decompressed PCM times.
+ *
+ * Solution:
+ * To solve this, we step through each compressed MP3 frames in
+ * the MP3 file (without decoding the actual audio) and keep a record of the
+ * decompressed "PCM" times for each frame. We save this relationship to
+ * to a local fie, called a fast-seek look-up table, which we can quickly
+ * reuse every subsequent time we need to seek within the MP3 file. This allows
+ * seeks to be performed extremely fast while being PCM-exact.
+ *
+ * This "fast-seek" file can hold data for multiple MP3s to avoid
+ * creating an excessive number of files in the local working directory.
+ *
+ * Challenges:
+ * 1. What happens if an MP3 file is changed but the MP3's filename remains the same?
+ *
+ * The lookup table is indexed based on a checksum instead of filename.
+ * The checksum is calculated based on a subset of the MP3's content in
+ * addition to being seeded based on the MP3's size in bytes.
+ * This makes it very sensitive to changes in MP3 content; if a change
+ * is detected a new lookup table is generated.
+ *
+ * 2. Checksums can be weak, what if a collision happens?
+ *
+ * To avoid the risk of collision, we use the current best-of-breed
+ * xxHash algorithm that has a quality-score of 10, the highest rating
+ * from the SMHasher test set. See https://github.com/Cyan4973/xxHash
+ * for more details.
+ *
+ * 3. What happens if fast-seek file is brought from a little-endian
+ * machine to a big-endian machine (x86 or ARM to a PowerPC or Sun
+ * Sparc machine)?
+ *
+ * The lookup table is serialized and multi-byte types are byte-swapped
+ * at runtime according to the architecture. This makes fast-seek files
+ * cross-compatible regardless of where they were written to or read from.
+ *
+ * 4. What happens if this code is updated to use a new fast-seek file
+ * format, but an old fast-seek file exists?
+ *
+ * The seek-table file is versioned (see SEEK_TABLE_IDENTIFIER befow),
+ * therefore, if the format and version is updated, then the seek-table
+ * will be regenerated.
+
+ * The seek table handler makes use of the following single-header public libraries:
+ * - dr_mp3: http://mackron.github.io/dr_mp3.html, by David Reid
+ * - archive: https://github.com/voidah/archive, by Arthur Ouellet
+ * - xxHash: http://cyan4973.github.io/xxHash, by Yann Collet
+ *
+ * This seek table handler is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DOSBox. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+// System headers
+#include <sys/stat.h>
+#include <fstream>
+#include <string>
+#include <map>
+
+// Local headers
+#include "xxhash.h"
+#include "../../../include/logging.h"
+#include "mp3_seek_table.h"
+
+// C++ scope modifiers
+using std::map;
+using std::vector;
+using std::string;
+using std::ios_base;
+using std::ifstream;
+using std::ofstream;
+
+// Identifies a valid versioned seek-table
+#define SEEK_TABLE_IDENTIFIER "st-v3"
+
+// How many compressed MP3 frames should we skip between each recorded
+// time point. The trade-off is as follows:
+// - a large number means slower in-game seeking but a smaller fast-seek file.
+// - a smaller numbers (below 10) results in fast seeks on slow hardware.
+#define FRAMES_PER_SEEK_POINT 7
+
+// Returns the size of a file in bytes (if valid), otherwise 0
+const size_t get_file_size(const char* filename) {
+ struct stat stat_buf;
+ int rc = stat(filename, &stat_buf);
+ return rc == 0 ? stat_buf.st_size : -1;
+}
+
+
+// Calculates a unique 64-bit hash (integer) from the provided file.
+// This function should not cause side-effects; ie, the current
+// read-position within the file should not be altered.
+//
+// This function tries to files as-close to the middle of the MP3 file as possible,
+// and use that feed the hash function in hopes of the most uniqueness.
+// We're trying to avoid content that might be duplicated across MP3s, like:
+// 1. ID3 tag filler content, which might be boiler plate or all empty
+// 2. Trailing silence or similar zero-PCM content
+//
+const Uint64 calculate_stream_hash(struct SDL_RWops* const context) {
+
+ // Save the current stream position, so we can restore it at the end of the function.
+ const Sint64 original_pos = SDL_RWtell(context);
+
+ // Seek to the end of the file so we can calculate the stream size.
+ SDL_RWseek(context, 0, RW_SEEK_END);
+
+ const Sint32 stream_size = (Sint32) SDL_RWtell(context);
+ if (stream_size <= 0) {
+ LOG_MSG("MP3: get_stream_size returned %d, but should be positive", stream_size);
+ return 0;
+ }
+
+ // Seek to the middle of the file while taking into account version small files.
+ const Uint32 tail_size = (stream_size > 32768) ? 32768 : stream_size;
+ const Sint64 mid_pos = static_cast<Sint64>(stream_size/2.0) - tail_size;
+ SDL_RWseek(context, mid_pos >= 0 ? mid_pos : 0, RW_SEEK_SET);
+
+ // Prepare our read buffer and counter:
+ vector<char> buffer(1024, 0);
+ Uint32 total_bytes_read = 0;
+
+ // Initialize xxHash's state using the stream_size as our seed.
+ // Seeding with the stream_size provide a second level of uniqueness
+ // in the unlikely scenario that two files of different length happen to
+ // have the same trailing 32KB of content. The different seeds will produce
+ // unique hashes.
+ XXH64_state_t* const state = XXH64_createState();
+ const Uint64 seed = stream_size;
+ XXH64_reset(state, seed);
+
+ while (total_bytes_read < tail_size) {
+ // Read a chunk of data.
+ const size_t bytes_read = SDL_RWread(context, buffer.data(), 1, buffer.size());
+
+ if (bytes_read != 0) {
+ // Update our hash if we read data.
+ XXH64_update(state, buffer.data(), bytes_read);
+ total_bytes_read += bytes_read;
+ } else {
+ break;
+ }
+ }
+
+ // restore the stream position
+ SDL_RWseek(context, original_pos, RW_SEEK_SET);
+
+ const Uint64 hash = XXH64_digest(state);
+ XXH64_freeState(state);
+ return hash;
+}
+
+// This function generates a new seek-table for a given mp3 stream and writes
+// the data to the fast-seek file.
+//
+const Uint64 generate_new_seek_points(const char* filename,
+ const Uint64& stream_hash,
+ drmp3* const p_dr,
+ map<Uint64, vector<drmp3_seek_point_serial> >& seek_points_table,
+ map<Uint64, drmp3_uint64>& pcm_frame_count_table,
+ vector<drmp3_seek_point_serial>& seek_points_vector) {
+
+ // Initialize our frame counters with zeros.
+ drmp3_uint64 mp3_frame_count(0);
+ drmp3_uint64 pcm_frame_count(0);
+
+ // Get the number of compressed MP3 frames and the number of uncompressed PCM frames.
+ drmp3_bool8 result = drmp3_get_mp3_and_pcm_frame_count(p_dr,
+ &mp3_frame_count,
+ &pcm_frame_count);
+
+ if ( result != DRMP3_TRUE
+ || mp3_frame_count < FRAMES_PER_SEEK_POINT
+ || pcm_frame_count < FRAMES_PER_SEEK_POINT) {
+ LOG_MSG("MP3: failed to determine or find sufficient mp3 and pcm frames");
+ return 0;
+ }
+
+ // Based on the number of frames found in the file, we size our seek-point
+ // vector accordingly. We then pass our sized vector into dr_mp3 which populates
+ // the decoded PCM times.
+ // We also take into account the desired number of "FRAMES_PER_SEEK_POINT",
+ // which is defined above.
+ drmp3_uint32 num_seek_points = mp3_frame_count/FRAMES_PER_SEEK_POINT + 1;
+ seek_points_vector.resize(num_seek_points);
+ result = drmp3_calculate_seek_points(p_dr,
+ &num_seek_points,
+ reinterpret_cast<drmp3_seek_point*>(seek_points_vector.data()));
+
+ if (result != DRMP3_TRUE || num_seek_points == 0) {
+ LOG_MSG("MP3: failed to calculate sufficient seek points for stream");
+ return 0;
+ }
+
+ // The calculate function provides us with the actual number of generated seek
+ // points in the num_seek_points variable; so if this differs from expected then we
+ // need to resize (ie: shrink) our vector.
+ if (num_seek_points != seek_points_vector.size())
+ seek_points_vector.resize(num_seek_points);
+
+ // Update our lookup table file with the new seek points and pcm_frame_count.
+ // Note: the serializer elegantly handles C++ STL objects and is endian-safe.
+ seek_points_table[stream_hash] = seek_points_vector;
+ pcm_frame_count_table[stream_hash] = pcm_frame_count;
+ ofstream outfile(filename, ios_base::trunc | ios_base::binary);
+ Archive<ofstream> serialize(outfile);
+ serialize << SEEK_TABLE_IDENTIFIER << seek_points_table << pcm_frame_count_table;
+ outfile.close();
+
+ // Finally, we return the number of decoded PCM frames for this given file, which
+ // doubles as a success-code.
+ return pcm_frame_count;
+}
+
+// This function attempts to fetch a seek-table for a given mp3 stream from the fast-seek file.
+// If anything is amiss then this function fails.
+//
+const Uint64 load_existing_seek_points(const char* filename,
+ const Uint64& stream_hash,
+ map<Uint64, vector<drmp3_seek_point_serial> >& seek_points_table,
+ map<Uint64, drmp3_uint64>& pcm_frame_count_table,
+ vector<drmp3_seek_point_serial>& seek_points) {
+
+ // The below sentinals sanity check and read the incoming
+ // file one-by-one until all the data can be trusted.
+
+ // Sentinal 1: bail if we got a zero-byte file.
+ struct stat buffer;
+ if (stat(filename, &buffer) != 0) {
+ return 0;
+ }
+
+ // Sentinal 2: Bail if the file isn't even big enough to hold our 4-byte header string.
+ const string expected_identifier(SEEK_TABLE_IDENTIFIER);
+ if (get_file_size(filename) < 4 + expected_identifier.length()) {
+ return 0;
+ }
+
+ // Sentinal 3: Bail if we don't get a match on our ID string.
+ string fetched_identifier;
+ ifstream infile(filename, ios_base::binary);
+ Archive<ifstream> deserialize(infile);
+ deserialize >> fetched_identifier;
+ if (fetched_identifier != expected_identifier) {
+ infile.close();
+ return 0;
+ }
+
+ // De-serialize the seek point and pcm_count tables.
+ deserialize >> seek_points_table >> pcm_frame_count_table;
+ infile.close();
+
+ // Sentinal 4: does the seek_points table have our stream's hash?
+ const auto p_seek_points = seek_points_table.find(stream_hash);
+ if (p_seek_points == seek_points_table.end()) {
+ return 0;
+ }
+
+ // Sentinal 5: does the pcm_frame_count table have our stream's hash?
+ const auto p_pcm_frame_count = pcm_frame_count_table.find(stream_hash);
+ if (p_pcm_frame_count == pcm_frame_count_table.end()) {
+ return 0;
+ }
+
+ // If we made it here, the file was valid and has lookup-data for our
+ // our desired stream
+ seek_points = p_seek_points->second;
+ return p_pcm_frame_count->second;
+}
+
+// This function attempts to populate our seek table for the given mp3 stream, first
+// attempting to read it from the fast-seek file and (if it can't be read for any reason), it
+// calculates new data. It makes use of the above two functions.
+//
+const Uint64 populate_seek_points(struct SDL_RWops* const context, mp3_t* p_mp3, const char* seektable_filename) {
+
+ // Calculate the stream's xxHash value.
+ Uint64 stream_hash = calculate_stream_hash(context);
+ if (stream_hash == 0) {
+ LOG_MSG("MP3: could not compute the hash of the stream");
+ return 0;
+ }
+
+ // Attempt to fetch the seek points and pcm count from an existing look up table file.
+ map<Uint64, vector<drmp3_seek_point_serial> > seek_points_table;
+ map<Uint64, drmp3_uint64> pcm_frame_count_table;
+ drmp3_uint64 pcm_frame_count = load_existing_seek_points(seektable_filename,
+ stream_hash,
+ seek_points_table,
+ pcm_frame_count_table,
+ p_mp3->seek_points_vector);
+
+ // Otherwise calculate new seek points and save them to the fast-seek file.
+ if (pcm_frame_count == 0) {
+ pcm_frame_count = generate_new_seek_points(seektable_filename,
+ stream_hash,
+ p_mp3->p_dr,
+ seek_points_table,
+ pcm_frame_count_table,
+ p_mp3->seek_points_vector);
+ if (pcm_frame_count == 0) {
+ LOG_MSG("MP3: could not load existing or generate new seek points for the stream");
+ return 0;
+ }
+ }
+
+ // Finally, regardless of which scenario succeeded above, we now have our seek points!
+ // We bind our seek points to the dr_mp3 object which will be used for fast seeking.
+ drmp3_bool8 result = drmp3_bind_seek_table(p_mp3->p_dr,
+ p_mp3->seek_points_vector.size(),
+ reinterpret_cast<drmp3_seek_point*>(p_mp3->seek_points_vector.data()));
+ if (result != DRMP3_TRUE) {
+ LOG_MSG("MP3: could not bind the seek points to the dr_mp3 object");
+ return 0;
+ }
+ return pcm_frame_count;
+}
diff --git a/src/libs/decoders/mp3_seek_table.h b/src/libs/decoders/mp3_seek_table.h
new file mode 100644
index 000000000..52f5e3a40
--- /dev/null
+++ b/src/libs/decoders/mp3_seek_table.h
@@ -0,0 +1,57 @@
+/**
+ * DOSBox MP3 Seek Table handler, Copyright 2018-2019 Kevin R. Croft (krcroft@gmail.com)
+ * See mp3_seek_table.cpp for more documentation.
+ *
+ * The seek table handler makes use of the following single-header public libraries:
+ * - dr_mp3: http://mackron.github.io/dr_mp3.html, by David Reid
+ * - archive: https://github.com/voidah/archive, by Arthur Ouellet
+ * - xxHash: http://cyan4973.github.io/xxHash, by Yann Collet
+ *
+ * This seek table handler is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DOSBox. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <vector> // provides: vector
+#include <SDL.h> // provides: SDL_RWops
+#include "archive.h" // provides: archive
+
+// Ensure we only get the API
+#ifdef DR_MP3_IMPLEMENTATION
+# undef DR_MP3_IMPLEMENTATION
+#endif
+#include "dr_mp3.h" // provides: drmp3
+
+// Note: this C++ struct must match (in binary-form) the "drmp3_seek_point" struct
+// defined in dr_mp3.h. If that changes, then update this to match, along
+// with adjusting the Serialize() template function that union's the values.
+//
+struct drmp3_seek_point_serial {
+ drmp3_uint64 seekPosInBytes; // Points to the first byte of an MP3 frame.
+ drmp3_uint64 pcmFrameIndex; // The index of the PCM frame this seek point targets.
+ drmp3_uint16 mp3FramesToDiscard; // The number of whole MP3 frames to be discarded before pcmFramesToDiscard.
+ drmp3_uint16 pcmFramesToDiscard;
+ template <class T> void Serialize(T& archive) {
+ archive & seekPosInBytes & pcmFrameIndex & mp3FramesToDiscard & pcmFramesToDiscard;
+ }
+};
+
+// Our private-decoder structure where we hold:
+// - a pointer to the working dr_mp3 instance
+// - a template vector of seek_points (the serializeable form)
+struct mp3_t {
+ drmp3* p_dr; // the actual drmp3 instance we open, read, and seek within
+ std::vector<drmp3_seek_point_serial> seek_points_vector;
+};
+
+const Uint64 populate_seek_points(struct SDL_RWops* const context, mp3_t* p_mp3, const char* seektable_filename);
diff --git a/src/libs/decoders/opus.c b/src/libs/decoders/opus.c
new file mode 100644
index 000000000..93ff05414
--- /dev/null
+++ b/src/libs/decoders/opus.c
@@ -0,0 +1,665 @@
+/*
+ * This DOSBox Ogg Opus decoder backend is written and copyright 2019 Kevin R Croft (krcroft@gmail.com)
+ *
+ * This decoders makes use of:
+ * - libopusfile, for .opus file handing and frame decoding
+ * - speexdsp, for resampling to the original input rate, if needed
+ *
+ * Source links
+ * - libogg: https://github.com/xiph/ogg
+ * - libopus: https://github.com/xiph/opus
+ * - opusfile: https://github.com/xiph/opusfile
+ * - speexdsp: https://github.com/xiph/speexdsp
+ * - opus-tools: https://github.com/xiph/opus-tools
+
+ * Documentation references
+ * - Ogg Opus: https://www.opus-codec.org/docs
+ * - OpusFile: https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/index.html
+ * - Resampler: https://www.speex.org/docs/manual/speex-manual/node7.html
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This DOSBox Ogg Opus decoder backend is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This DOSBox Ogg Opus decoder backend is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with DOSBox. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdlib.h> // getenv()
+
+#include "SDL_sound.h"
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h"
+
+#include "internal/opusfile/include/opusfile.h"
+#include "internal/speexdsp/include/speex/speex_resampler.h"
+
+// The minimum buffer samples per channel: 120 ms @ 48 samples/ms, defined by opus
+#define OPUS_MIN_BUFFER_SAMPLES_PER_CHANNEL 5760
+
+// Opus's internal sample rates, to which all encoded streams get resampled
+#define OPUS_SAMPLE_RATE 48000
+#define OPUS_SAMPLE_RATE_PER_MS 48
+
+static Sint32 opus_init (void);
+static void opus_quit (void);
+static Sint32 opus_open (Sound_Sample* sample, const char* ext);
+static void opus_close (Sound_Sample* sample);
+static Uint32 opus_read (Sound_Sample* sample);
+static Sint32 opus_rewind (Sound_Sample* sample);
+static Sint32 opus_seek (Sound_Sample* sample, const Uint32 ms);
+
+static const char* extensions_opus[] = { "OPUS", NULL };
+
+const Sound_DecoderFunctions __Sound_DecoderFunctions_OPUS =
+{
+ {
+ extensions_opus,
+ "Ogg Opus audio using libopusfile",
+ "Kevin R Croft <krcroft@gmail.com>",
+ "https://www.opus-codec.org/"
+ },
+
+ opus_init, /* init() method */
+ opus_quit, /* quit() method */
+ opus_open, /* open() method */
+ opus_close, /* close() method */
+ opus_read, /* read() method */
+ opus_rewind, /* rewind() method */
+ opus_seek /* seek() method */
+};
+
+
+// Our private-decoder structure where we hold the opusfile, resampler,
+// circular buffer, and buffer tracking variables.
+typedef struct
+{
+ Uint64 of_pcm; // absolute position in consumed Opus samples
+ OggOpusFile* of; // the actual opusfile we open/read/seek within
+ opus_int16* buffer; // pointer to the start of our circular buffer
+ SpeexResamplerState* resampler; // pointer to an instantiated resampler
+ float rate_ratio; // OPUS_RATE (48KHz) divided by desired sample rate
+ Uint16 buffer_size; // maximum number of samples we can hold in our buffer
+ Uint16 decoded; // number of samples decoded in our buffer
+ Uint16 consumed; // number of samples consumed in our buffer
+ Uint16 frame_size; // number of samples decoded in one opus frame
+ SDL_bool eof; // indicates if we've hit end-of-file decoding
+} opus_t;
+
+
+static Sint32 opus_init(void)
+{
+ SNDDBG(("Opus init: done\n"));
+ return 1; /* always succeeds. */
+} /* opus_init */
+
+
+static void opus_quit(void){
+ SNDDBG(("Opus quit: done\n"));
+} // no-op
+
+
+/*
+ * Read-Write Ops Read Callback Wrapper
+ * ====================================
+ *
+ * OPUS: typedef int(*op_read_func)
+ * void* _stream --> The stream to read from
+ * unsigned char* _ptr --> The buffer to store the data in
+ * int _nbytes --> The maximum number of bytes to read.
+ * Returns: The number of bytes successfully read, or a negative value on error.
+ *
+ * SDL: size_t SDL_RWread
+ * struct SDL_RWops* context --> a pointer to an SDL_RWops structure
+ * void* ptr --> a pointer to a buffer to read data into
+ * size_t size --> the size of each object to read, in bytes
+ * size_t maxnum --> the maximum number of objects to be read
+ */
+static Sint32 RWops_opus_read(void* stream, unsigned char* ptr, Sint32 nbytes)
+{
+ const Sint32 bytes_read = SDL_RWread((SDL_RWops*)stream,
+ (void*)ptr,
+ sizeof(unsigned char),
+ (size_t)nbytes);
+ SNDDBG(("Opus ops read: "
+ "{wanted: %d, returned: %ld}\n", nbytes, bytes_read));
+
+ return bytes_read;
+} /* RWops_opus_read */
+
+
+/*
+ * Read-Write Ops Seek Callback Wrapper
+ * ====================================
+ *
+ * OPUS: typedef int(* op_seek_func)
+ * void* _stream, --> The stream to seek in
+ * opus_int64 _offset, --> Sets the position indicator for _stream in bytes
+ * int _whence --> If whence is set to SEEK_SET, SEEK_CUR, or SEEK_END,
+ * the offset is relative to the start of the stream,
+ * the current position indicator, or end-of-file,
+ * respectively
+ * Returns: 0 Success, or -1 if seeking is not supported or an error occurred.
+ * define SEEK_SET 0
+ * define SEEK_CUR 1
+ * define SEEK_END 2
+ *
+ * SDL: Sint64 SDL_RWseek
+ * SDL_RWops* context --> a pointer to an SDL_RWops structure
+ * Sint64 offset, --> offset, in bytes
+ * Sint32 whence --> an offset in bytes, relative to whence location; can be negative
+ * Returns the final offset in the data stream after the seek or -1 on error.
+ * RW_SEEK_SET 0
+ * RW_SEEK_CUR 1
+ * RW_SEEK_END 2
+ */
+static Sint32 RWops_opus_seek(void* stream, const opus_int64 offset, const Sint32 whence)
+{
+ const Sint64 offset_after_seek = SDL_RWseek((SDL_RWops*)stream, offset, whence);
+
+ SNDDBG(("Opus ops seek: "
+ "{requested offset: %ld, seeked offset: %ld}\n",
+ offset, offset_after_seek));
+
+ return (offset_after_seek != -1 ? 0 : -1);
+} /* RWops_opus_seek */
+
+
+/*
+ * Read-Write Ops Close Callback Wrapper
+ * =====================================
+ * OPUS: typedef int(* op_close_func)(void *_stream)
+ * SDL: Sint32 SDL_RWclose(struct SDL_RWops* context)
+ */
+static Sint32 RWops_opus_close(void* stream)
+{
+ /* SDL closes this for us */
+ // return SDL_RWclose((SDL_RWops*)stream);
+ return 0;
+} /* RWops_opus_close */
+
+
+/*
+ * Read-Write Ops Tell Callback Wrapper
+ * ====================================
+ * OPUS: typedef opus_int64(* op_tell_func)(void *_stream)
+ * SDL: Sint64 SDL_RWtell(struct SDL_RWops* context)
+ */
+static opus_int64 RWops_opus_tell(void* stream)
+{
+ const Sint64 current_offset = SDL_RWtell((SDL_RWops*)stream);
+
+ SNDDBG(("Opus ops tell: "
+ "%ld\n", current_offset));
+
+ return current_offset;
+} /* RWops_opus_tell */
+
+
+// Populate the opus callback object (in perscribed order), with our callback functions.
+static const OpusFileCallbacks RWops_opus_callbacks =
+{
+ .read = RWops_opus_read,
+ .seek = RWops_opus_seek,
+ .tell = RWops_opus_tell,
+ .close = RWops_opus_close
+};
+
+/* Return a human readable version of an OpusFile error code... */
+#if (defined DEBUG_CHATTER)
+static const char* opus_error(const Sint8 errnum)
+{
+ switch(errnum)
+ {
+ case OP_FALSE: // -1
+ return("A request did not succeed");
+ case OP_EOF: // -2
+ return("End of file");
+ case OP_HOLE: // -3
+ return("There was a hole in the page sequence numbers "
+ "(e.g., a page was corrupt or missing)");
+ case OP_EREAD: // -128
+ return("An underlying read, seek, or tell operation "
+ "failed when it should have succeeded");
+ case OP_EFAULT: // -129
+ return("A NULL pointer was passed where one was unexpected, or an "
+ "internal memory allocation failed, or an internal library "
+ "error was encountered");
+ case OP_EIMPL: // -130
+ return("The stream used a feature that is not implemented,"
+ " such as an unsupported channel family");
+ case OP_EINVAL: // -131
+ return("One or more parameters to a function were invalid");
+ case OP_ENOTFORMAT: // -132
+ return("A purported Ogg Opus stream did not begin with an Ogg page, a "
+ "purported header packet did not start with one of the required "
+ "strings, `OpusHead` or `OpusTags`, or a link in a chained file "
+ "was encoun tered that did not contain any logical Opus streams");
+ case OP_EBADHEADER: // -133
+ return("A required header packet was not properly formatted, contained illegal "
+ "values, or was missing altogether");
+ case OP_EVERSION: // -134
+ return("The ID header contained an unrecognized version number");
+ case OP_ENOTAUDIO: // -135
+ return("Not valid audio");
+ case OP_EBADPACKET: // -136
+ return("An audio packet failed to decode properly");
+ case OP_EBADLINK: // -137
+ return("We failed to find data we had seen before, or the bitstream structure was "
+ "sufficiently malformed that seeking to the target destination was impossible");
+ case OP_ENOSEEK: // -138
+ return("An operation that requires seeking was requested on an unseekable stream");
+ case OP_EBADTIMESTAMP: // -139
+ return("The first or last granule position of a link failed basic validity checks");
+ } /* switch */
+ return "unknown error";
+} /* opus_error */
+#endif
+
+static __inline__ void output_opus_info(const OggOpusFile* of, const OpusHead* oh)
+{
+#if (defined DEBUG_CHATTER)
+ const OpusTags* ot = op_tags(of, -1);
+
+ // Guard
+ if ( of == NULL
+ || oh == NULL
+ || ot == NULL) return;
+
+ // Dump info
+ SNDDBG(("Opus serial number: %u\n", op_serialno(of, -1)));
+ SNDDBG(("Opus format version: %d\n", oh->version));
+ SNDDBG(("Opus channel count: %d\n", oh->channel_count ));
+ SNDDBG(("Opus seekable: %s\n", op_seekable(of) ? "True" : "False"));
+ SNDDBG(("Opus pre-skip samples: %u\n", oh->pre_skip));
+ SNDDBG(("Opus input sample rate: %u\n", oh->input_sample_rate));
+ SNDDBG(("Opus logical streams: %d\n", oh->stream_count));
+ SNDDBG(("Opus vendor: %s\n", ot->vendor));
+ for (int i = 0; i < ot->comments; i++)
+ SNDDBG(("Opus: user comment: '%s'\n", ot->user_comments[i]));
+#endif
+} /* output_opus_comments */
+
+/*
+ * Opus Open
+ * =========
+ * - Creates a new opus file object by using our our callback structure for all IO operations.
+ * - We also intialize and allocate memory for fields in the opus_t decode structure.
+ * - SDL expects a returns of 1 on success
+ */
+static Sint32 opus_open(Sound_Sample* sample, const char* ext)
+{
+ Sint32 rcode;
+ Sound_SampleInternal* internal = (Sound_SampleInternal*)sample->opaque;
+
+ // Open the Opus File and print some info
+ OggOpusFile* of = op_open_callbacks(internal->rw, &RWops_opus_callbacks, NULL, 0, &rcode);
+ if (rcode != 0) {
+ op_free(of);
+ of = NULL;
+ SNDDBG(("Opus open error: "
+ "'Could not open opus file: %s'\n", opus_error(rcode)));
+ BAIL_MACRO("Opus open fatal: 'Not a valid Ogg Opus file'", 0);
+ }
+ const OpusHead* oh = op_head(of, -1);
+ output_opus_info(of, oh);
+
+ // Initialize our decoder struct elements
+ opus_t* decoder = SDL_malloc(sizeof(opus_t));
+ decoder->of = of;
+ decoder->of_pcm = 0;
+ decoder->decoded = 0;
+ decoder->consumed = 0;
+ decoder->frame_size = 0;
+ decoder->eof = SDL_FALSE;
+ decoder->buffer = NULL;
+
+ // Connect our long-lived internal decoder to the one we're building here
+ internal->decoder_private = decoder;
+
+ if ( sample->desired.rate != 0
+ && sample->desired.rate != OPUS_SAMPLE_RATE
+ && getenv("SDL_DONT_RESAMPLE") == NULL){
+
+ // Opus resamples all inputs to 48kHz. By default (if env-var SDL_DONT_RESAMPLE doesn't exist)
+ // we resample to the desired rate so the recieving SDL_sound application doesn't have to.
+ // This avoids breaking applications that don't expect 48kHz audio and also gives us
+ // quality-control by using the speex resampler, which has a noise floor of -140 dB, which
+ // is ~40dB lower than the -96dB offered by 16-bit CD-quality audio.
+ //
+ sample->actual.rate = sample->desired.rate;
+ decoder->rate_ratio = OPUS_SAMPLE_RATE / (float)(sample->desired.rate);
+ decoder->resampler = speex_resampler_init(oh->channel_count,
+ OPUS_SAMPLE_RATE,
+ sample->desired.rate,
+ // SPEEX_RESAMPLER_QUALITY_VOIP, // consumes ~20 Mhz
+ SPEEX_RESAMPLER_QUALITY_DEFAULT, // consumes ~40 Mhz
+ // SPEEX_RESAMPLER_QUALITY_DESKTOP, // consumes ~80 Mhz
+ &rcode);
+
+ // If we failed to initialize the resampler, then tear down
+ if (rcode < 0) {
+ opus_close(sample);
+ BAIL_MACRO("Opus: failed initializing the resampler", 0);
+ }
+
+ // Otherwise use native sampling
+ } else {
+ sample->actual.rate = OPUS_SAMPLE_RATE;
+ decoder->rate_ratio = 1.0;
+ decoder->resampler = NULL;
+ }
+
+ // Allocate our buffer to hold PCM samples from the Opus decoder
+ decoder->buffer_size = oh->channel_count * OPUS_MIN_BUFFER_SAMPLES_PER_CHANNEL * 1.5;
+ decoder->buffer = SDL_malloc(decoder->buffer_size * sizeof(opus_int16));
+
+ // Gather static properties about our stream (channels, seek-ability, format, and duration)
+ sample->actual.channels = (Uint8)(oh->channel_count);
+ sample->flags = op_seekable(of) ? SOUND_SAMPLEFLAG_CANSEEK: 0;
+ sample->actual.format = AUDIO_S16LSB; // returns least-significant-byte order regardless of architecture
+
+ ogg_int64_t total_time = op_pcm_total(of, -1); // total PCM samples in the stream
+ internal->total_time = total_time == OP_EINVAL ? -1 : // total milliseconds in the stream
+ (Sint32)( (double)total_time / OPUS_SAMPLE_RATE_PER_MS);
+
+ return 1;
+} /* opus_open */
+
+
+/*
+ * Opus Close
+ * ==========
+ * Free and NULL all allocated memory pointers.
+ */
+static void opus_close(Sound_Sample* sample)
+{
+ /* From the Opus docs: if opening a stream/file/or using op_test_callbacks() fails
+ * then we are still responsible for freeing the OggOpusFile with op_free().
+ */
+ Sound_SampleInternal* internal = (Sound_SampleInternal*) sample->opaque;
+
+ opus_t* d = internal->decoder_private;
+ if (d != NULL) {
+
+ if (d->of != NULL) {
+ op_free(d->of);
+ d->of = NULL;
+ }
+
+ if(d->resampler != NULL) {
+ speex_resampler_destroy(d->resampler);
+ d->resampler = NULL;
+ }
+
+ if(d->buffer != NULL) {
+ SDL_free(d->buffer);
+ d->buffer = NULL;
+ }
+
+ SDL_free(d);
+ d = NULL;
+ }
+ return;
+
+} /* opus_close */
+
+
+/*
+ * Opus Read
+ * =========
+ * Decode, resample (if needed), and write the output to the
+ * requested buffer.
+ */
+static Uint32 opus_read(Sound_Sample* sample)
+{
+ Sound_SampleInternal* internal = (Sound_SampleInternal*) sample->opaque;
+ opus_t* d = internal->decoder_private;
+
+ opus_int16* output_buffer = internal->buffer;
+ const Uint16 requested_output_size = internal->buffer_size / sizeof(opus_int16);
+ const Uint16 derived_consumption_size = (requested_output_size * d->rate_ratio) + 0.5;
+
+ // Three scenarios in order of probabilty:
+ //
+ // 1. consume: resample (if needed) a chunk from our decoded queue
+ // sufficient to fill the requested buffer.
+ //
+ // If the decoder has hit the end-of-file, drain any
+ // remaining decoded data before setting the EOF flag.
+ //
+ // 2. decode: decode chunks unil our buffer is full or we hit EOF.
+ //
+ // 3. wrap: we've decoded and consumed to edge of our buffer
+ // so wrap any remaining decoded samples back around.
+
+ Sint32 rcode = 1;
+ SDL_bool have_consumed = SDL_FALSE;
+ while (! have_consumed){
+
+ // consume ...
+ const Uint16 unconsumed_size = d->decoded - d->consumed;
+ if (unconsumed_size >= derived_consumption_size || d->eof) {
+
+ // If we're at the start of the stream, ignore 'pre-skip' samples
+ // per-channel. Pre-skip describes how much data must be decoded
+ // before valid output is obtained.
+ //
+ const OpusHead* oh = op_head(d->of, -1);
+ if (d->of_pcm == 0)
+ d->consumed += oh->pre_skip * oh->channel_count;
+
+ // We use these to record the actual consumed and output sizes
+ Uint32 actual_consumed_size = unconsumed_size;
+ Uint32 actual_output_size = requested_output_size;
+
+ // If we need to resample
+ if (d->resampler)
+ (void) speex_resampler_process_int(d->resampler, 0,
+ d->buffer + d->consumed,
+ &actual_consumed_size,
+ output_buffer,
+ &actual_output_size);
+
+ // Otherwise copy the bytes
+ else {
+ if (unconsumed_size < requested_output_size)
+ actual_output_size = unconsumed_size;
+ actual_consumed_size = actual_output_size;
+ SDL_memcpy(output_buffer, d->buffer + d->consumed, actual_output_size * sizeof(opus_int16));
+ }
+
+ // bump our comsumption count and absolute pcm position
+ d->consumed += actual_consumed_size;
+ d->of_pcm += actual_consumed_size;
+
+ SNDDBG(("Opus read consuming: "
+ "{output: %u, so_far: %u, remaining_buffer: %u}\n",
+ actual_output_size, d->consumed, d->decoded - d->consumed));
+
+ // if we wrote less than requested then we're at the end-of-file
+ if (actual_output_size < requested_output_size) {
+ sample->flags |= SOUND_SAMPLEFLAG_EOF;
+ SNDDBG(("Opus read consuming: "
+ "{end_of_buffer: True, requested: %u, resampled_output: %u}\n",
+ requested_output_size, actual_output_size));
+ }
+
+ rcode = actual_output_size * sizeof(opus_int16); // covert from samples to bytes
+ have_consumed = SDL_TRUE;
+ }
+
+ else {
+ // wrap ...
+ if (d->frame_size > 0) {
+ SDL_memcpy(d->buffer,
+ d->buffer + d->consumed,
+ (d->decoded - d->consumed)*sizeof(opus_int16));
+
+ d->decoded -= d->consumed;
+ d->consumed = 0;
+
+ SNDDBG(("Opus read wrapping: "
+ "{wrapped: %u}\n", d->decoded));
+ }
+
+ // decode ...
+ while (rcode > 0 && d->buffer_size - d->decoded >= d->frame_size) {
+
+ rcode = sample->actual.channels * op_read(d->of,
+ d->buffer + d->decoded,
+ d->buffer_size - d->decoded, NULL);
+ // Use the largest decoded frame to know when
+ // our buffer is too small to hold a frame, to
+ // avoid constraining the decoder to fill sizes
+ // smaller than the stream's frame-size
+ if (rcode > d->frame_size){
+
+ SNDDBG(("Opus read decoding: "
+ "{frame_previous: %u, frame_new: %u}\n",
+ d->frame_size, rcode));
+
+ d->frame_size = rcode;
+ }
+
+ // assess the validity of the return code
+ if (rcode > 0) d->decoded += rcode; // reading
+ else if (rcode == 0) d->eof = SDL_TRUE; // done
+ else if (rcode == OP_HOLE) rcode = 1; // hole in the data, carry on
+ else // (rcode < 0) // error
+ sample->flags |= SOUND_SAMPLEFLAG_ERROR;
+
+ SNDDBG(("Opus read decoding: "
+ "{decoded: %u, remaining buffer: %u, end_of_file: %s}\n",
+ rcode, d->buffer_size - d->decoded, d->eof ? "True" : "False"));
+ }
+ }
+ } // end while.
+ return rcode;
+} /* opus_read */
+
+
+/*
+ * Opus Rewind
+ * ===========
+ * Sets the current position of the stream to 0.
+ */
+static Sint32 opus_rewind(Sound_Sample* sample)
+{
+ const Sint32 rcode = opus_seek(sample, 0);
+ BAIL_IF_MACRO(rcode < 0, ERR_IO_ERROR, 0);
+ return rcode;
+} /* opus_rewind */
+
+
+/*
+ * Opus Seek
+ * =========
+ * Set the current position of the stream to the indicated
+ * integer offset in milliseconds.
+ */
+static Sint32 opus_seek(Sound_Sample* sample, const Uint32 ms)
+{
+ Sound_SampleInternal* internal = (Sound_SampleInternal*) sample->opaque;
+ opus_t* d = internal->decoder_private;
+ int rcode = -1;
+
+ #if (defined DEBUG_CHATTER)
+ const float total_seconds = (float)ms/1000;
+ uint8_t minutes = total_seconds / 60;
+ const float seconds = ((int)total_seconds % 60) + (total_seconds - (int)total_seconds);
+ const uint8_t hours = minutes / 60;
+ minutes = minutes % 60;
+ #endif
+
+ // convert the desired ms offset into OPUS PCM samples
+ const ogg_int64_t desired_pcm = ms * OPUS_SAMPLE_RATE_PER_MS;
+
+ // Is our stream already positioned at the requested offset?
+ if (d->of_pcm == desired_pcm){
+
+ SNDDBG(("Opus seek avoided: "
+ "{requested_time: '%02d:%02d:%.2f', becomes_opus_pcm: %ld, actual_pcm_pos: %ld}\n",
+ hours, minutes, seconds, desired_pcm, d->of_pcm));
+
+ rcode = 1;
+ }
+
+ // If not, check if we can jump within our circular buffer (and not actually seek!)
+ // In this scenario, we don't have to waste our currently decoded samples
+ // or incur the cost of 80ms of pre-roll decoding behind the scene in libopus.
+ else {
+ Uint64 pcm_start = d->of_pcm - d->consumed;
+ Uint64 pcm_end = pcm_start + d->decoded;
+
+ // In both scenarios below we're going to seek, in which case
+ // our sample flags should be reset and let the read function
+ // re-assess the flag.
+ //
+
+ // Is the requested pcm offset within our decoded range?
+ if (desired_pcm >= pcm_start && desired_pcm <= pcm_end) {
+
+ SNDDBG(("Opus seek avoided: "
+ "{requested_time: '%02d:%02d:%.2f', becomes_opus_pcm: %ld, buffer_start: %ld, buffer_end: %ld}\n",
+ hours, minutes, seconds, desired_pcm, pcm_start, pcm_end));
+
+ // Yes, so simply adjust our existing pcm offset and consumption position
+ // No seeks or pre-roll needed!
+ d->consumed = desired_pcm - pcm_start;
+ d->of_pcm = desired_pcm;
+
+ // reset our sample flags and let our consumption state re-apply
+ // the flags per its own rules
+ if (op_seekable(d->of))
+ sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
+
+ // note, we don't reset d->eof because our decode state is unchanged
+ rcode = 1;
+ // rcode is 1, confirming we successfully seeked
+ }
+
+ // No; the requested pcm offset is outside our circular decode buffer,
+ // so actually seek and reset our decode and consumption counters.
+ else {
+ rcode = op_pcm_seek(d->of, desired_pcm) + 1;
+
+ // op_pcm_seek(..) returns 0, to which we add 1, on success
+ // ... or a negative value on error.
+ if (rcode > 0) {
+ d->of_pcm = desired_pcm;
+ d->consumed = 0;
+ d->decoded = 0;
+ d->eof = SDL_FALSE;
+ SNDDBG(("Opus seek in file: "
+ "{requested_time: '%02d:%02d:%.2f', becomes_opus_pcm: %ld}\n",
+ hours, minutes, seconds, desired_pcm));
+
+ // reset our sample flags and let the read function re-apply
+ // sample flags as it hits them from our our offset
+ if (op_seekable(d->of))
+ sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
+
+ }
+ // otherwise we failed to seek.. so leave everything as-is.
+ }
+ }
+
+ BAIL_IF_MACRO(rcode < 0, ERR_IO_ERROR, 0);
+ return rcode;
+} /* opus_seek */
+
+/* end of ogg_opus.c ... */
diff --git a/src/libs/decoders/stb.h b/src/libs/decoders/stb.h
new file mode 100644
index 000000000..1a7c7be5e
--- /dev/null
+++ b/src/libs/decoders/stb.h
@@ -0,0 +1,14453 @@
+/* stb.h - v2.35 - Sean's Tool Box -- public domain -- http://nothings.org/stb.h
+ no warranty is offered or implied; use this code at your own risk
+
+ This is a single header file with a bunch of useful utilities
+ for getting stuff done in C/C++.
+
+ Documentation: http://nothings.org/stb/stb_h.html
+ Unit tests: http://nothings.org/stb/stb.c
+
+
+ ============================================================================
+ You MUST
+
+ #define STB_DEFINE
+
+ in EXACTLY _one_ C or C++ file that includes this header, BEFORE the
+ include, like this:
+
+ #define STB_DEFINE
+ #include "stb.h"
+
+ All other files should just #include "stb.h" without the #define.
+ ============================================================================
+
+
+Version History
+
+ 2.35 fix clang-cl issues with swprintf
+ 2.34 fix warnings
+ 2.33 more fixes to random numbers
+ 2.32 stb_intcmprev, stb_uidict, fix random numbers on Linux
+ 2.31 stb_ucharcmp
+ 2.30 MinGW fix
+ 2.29 attempt to fix use of swprintf()
+ 2.28 various new functionality
+ 2.27 test _WIN32 not WIN32 in STB_THREADS
+ 2.26 various warning & bugfixes
+ 2.25 various warning & bugfixes
+ 2.24 various warning & bugfixes
+ 2.23 fix 2.22
+ 2.22 64-bit fixes from '!='; fix stb_sdict_copy() to have preferred name
+ 2.21 utf-8 decoder rejects "overlong" encodings; attempted 64-bit improvements
+ 2.20 fix to hash "copy" function--reported by someone with handle "!="
+ 2.19 ???
+ 2.18 stb_readdir_subdirs_mask
+ 2.17 stb_cfg_dir
+ 2.16 fix stb_bgio_, add stb_bgio_stat(); begin a streaming wrapper
+ 2.15 upgraded hash table template to allow:
+ - aggregate keys (explicit comparison func for EMPTY and DEL keys)
+ - "static" implementations (so they can be culled if unused)
+ 2.14 stb_mprintf
+ 2.13 reduce identifiable strings in STB_NO_STB_STRINGS
+ 2.12 fix STB_ONLY -- lots of uint32s, TRUE/FALSE things had crept in
+ 2.11 fix bug in stb_dirtree_get() which caused "c://path" sorts of stuff
+ 2.10 STB_F(), STB_I() inline constants (also KI,KU,KF,KD)
+ 2.09 stb_box_face_vertex_axis_side
+ 2.08 bugfix stb_trimwhite()
+ 2.07 colored printing in windows (why are we in 1985?)
+ 2.06 comparison functions are now functions-that-return-functions and
+ accept a struct-offset as a parameter (not thread-safe)
+ 2.05 compile and pass tests under Linux (but no threads); thread cleanup
+ 2.04 stb_cubic_bezier_1d, smoothstep, avoid dependency on registry
+ 2.03 ?
+ 2.02 remove integrated documentation
+ 2.01 integrate various fixes; stb_force_uniprocessor
+ 2.00 revised stb_dupe to use multiple hashes
+ 1.99 stb_charcmp
+ 1.98 stb_arr_deleten, stb_arr_insertn
+ 1.97 fix stb_newell_normal()
+ 1.96 stb_hash_number()
+ 1.95 hack stb__rec_max; clean up recursion code to use new functions
+ 1.94 stb_dirtree; rename stb_extra to stb_ptrmap
+ 1.93 stb_sem_new() API cleanup (no blockflag-starts blocked; use 'extra')
+ 1.92 stb_threadqueue--multi reader/writer queue, fixed size or resizeable
+ 1.91 stb_bgio_* for reading disk asynchronously
+ 1.90 stb_mutex uses CRITICAL_REGION; new stb_sync primitive for thread
+ joining; workqueue supports stb_sync instead of stb_semaphore
+ 1.89 support ';' in constant-string wildcards; stb_mutex wrapper (can
+ implement with EnterCriticalRegion eventually)
+ 1.88 portable threading API (only for win32 so far); worker thread queue
+ 1.87 fix wildcard handling in stb_readdir_recursive
+ 1.86 support ';' in wildcards
+ 1.85 make stb_regex work with non-constant strings;
+ beginnings of stb_introspect()
+ 1.84 (forgot to make notes)
+ 1.83 whoops, stb_keep_if_different wasn't deleting the temp file
+ 1.82 bring back stb_compress from stb_file.h for cmirror
+ 1.81 various bugfixes, STB_FASTMALLOC_INIT inits FASTMALLOC in release
+ 1.80 stb_readdir returns utf8; write own utf8-utf16 because lib was wrong
+ 1.79 stb_write
+ 1.78 calloc() support for malloc wrapper, STB_FASTMALLOC
+ 1.77 STB_FASTMALLOC
+ 1.76 STB_STUA - Lua-like language; (stb_image, stb_csample, stb_bilinear)
+ 1.75 alloc/free array of blocks; stb_hheap bug; a few stb_ps_ funcs;
+ hash*getkey, hash*copy; stb_bitset; stb_strnicmp; bugfix stb_bst
+ 1.74 stb_replaceinplace; use stdlib C function to convert utf8 to UTF-16
+ 1.73 fix performance bug & leak in stb_ischar (C++ port lost a 'static')
+ 1.72 remove stb_block, stb_block_manager, stb_decompress (to stb_file.h)
+ 1.71 stb_trimwhite, stb_tokens_nested, etc.
+ 1.70 back out 1.69 because it might problemize mixed builds; stb_filec()
+ 1.69 (stb_file returns 'char *' in C++)
+ 1.68 add a special 'tree root' data type for stb_bst; stb_arr_end
+ 1.67 full C++ port. (stb_block_manager)
+ 1.66 stb_newell_normal
+ 1.65 stb_lex_item_wild -- allow wildcard items which MUST match entirely
+ 1.64 stb_data
+ 1.63 stb_log_name
+ 1.62 stb_define_sort; C++ cleanup
+ 1.61 stb_hash_fast -- Paul Hsieh's hash function (beats Bob Jenkins'?)
+ 1.60 stb_delete_directory_recursive
+ 1.59 stb_readdir_recursive
+ 1.58 stb_bst variant with parent pointer for O(1) iteration, not O(log N)
+ 1.57 replace LCG random with Mersenne Twister (found a public domain one)
+ 1.56 stb_perfect_hash, stb_ischar, stb_regex
+ 1.55 new stb_bst API allows multiple BSTs per node (e.g. secondary keys)
+ 1.54 bugfix: stb_define_hash, stb_wildmatch, regexp
+ 1.53 stb_define_hash; recoded stb_extra, stb_sdict use it
+ 1.52 stb_rand_define, stb_bst, stb_reverse
+ 1.51 fix 'stb_arr_setlen(NULL, 0)'
+ 1.50 stb_wordwrap
+ 1.49 minor improvements to enable the scripting language
+ 1.48 better approach for stb_arr using stb_malloc; more invasive, clearer
+ 1.47 stb_lex (lexes stb.h at 1.5ML/s on 3Ghz P4; 60/70% of optimal/flex)
+ 1.46 stb_wrapper_*, STB_MALLOC_WRAPPER
+ 1.45 lightly tested DFA acceleration of regexp searching
+ 1.44 wildcard matching & searching; regexp matching & searching
+ 1.43 stb_temp
+ 1.42 allow stb_arr to use stb_malloc/realloc; note this is global
+ 1.41 make it compile in C++; (disable stb_arr in C++)
+ 1.40 stb_dupe tweak; stb_swap; stb_substr
+ 1.39 stb_dupe; improve stb_file_max to be less stupid
+ 1.38 stb_sha1_file: generate sha1 for file, even > 4GB
+ 1.37 stb_file_max; partial support for utf8 filenames in Windows
+ 1.36 remove STB__NO_PREFIX - poor interaction with IDE, not worth it
+ streamline stb_arr to make it separately publishable
+ 1.35 bugfixes for stb_sdict, stb_malloc(0), stristr
+ 1.34 (streaming interfaces for stb_compress)
+ 1.33 stb_alloc; bug in stb_getopt; remove stb_overflow
+ 1.32 (stb_compress returns, smaller&faster; encode window & 64-bit len)
+ 1.31 stb_prefix_count
+ 1.30 (STB__NO_PREFIX - remove stb_ prefixes for personal projects)
+ 1.29 stb_fput_varlen64, etc.
+ 1.28 stb_sha1
+ 1.27 ?
+ 1.26 stb_extra
+ 1.25 ?
+ 1.24 stb_copyfile
+ 1.23 stb_readdir
+ 1.22 ?
+ 1.21 ?
+ 1.20 ?
+ 1.19 ?
+ 1.18 ?
+ 1.17 ?
+ 1.16 ?
+ 1.15 stb_fixpath, stb_splitpath, stb_strchr2
+ 1.14 stb_arr
+ 1.13 ?stb, stb_log, stb_fatal
+ 1.12 ?stb_hash2
+ 1.11 miniML
+ 1.10 stb_crc32, stb_adler32
+ 1.09 stb_sdict
+ 1.08 stb_bitreverse, stb_ispow2, stb_big32
+ stb_fopen, stb_fput_varlen, stb_fput_ranged
+ stb_fcmp, stb_feq
+ 1.07 (stb_encompress)
+ 1.06 stb_compress
+ 1.05 stb_tokens, (stb_hheap)
+ 1.04 stb_rand
+ 1.03 ?(s-strings)
+ 1.02 ?stb_filelen, stb_tokens
+ 1.01 stb_tolower
+ 1.00 stb_hash, stb_intcmp
+ stb_file, stb_stringfile, stb_fgets
+ stb_prefix, stb_strlower, stb_strtok
+ stb_image
+ (stb_array), (stb_arena)
+
+Parenthesized items have since been removed.
+
+LICENSE
+
+ See end of file for license information.
+
+CREDITS
+
+ Written by Sean Barrett.
+
+ Fixes:
+ Philipp Wiesemann
+ Robert Nix
+ r-lyeh
+ blackpawn
+ github:Mojofreem
+ Ryan Whitworth
+ Vincent Isambart
+ Mike Sartain
+ Eugene Opalev
+ Tim Sjostrand
+ github:infatum
+ Dave Butler (Croepha)
+ Ethan Lee (flibitijibibo)
+*/
+
+#include <stdarg.h>
+
+#ifndef STB__INCLUDE_STB_H
+#define STB__INCLUDE_STB_H
+
+#define STB_VERSION 1
+
+#ifdef STB_INTROSPECT
+ #define STB_DEFINE
+#endif
+
+#ifdef STB_DEFINE_THREADS
+ #ifndef STB_DEFINE
+ #define STB_DEFINE
+ #endif
+ #ifndef STB_THREADS
+ #define STB_THREADS
+ #endif
+#endif
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+ #ifndef _CRT_SECURE_NO_WARNINGS
+ #define _CRT_SECURE_NO_WARNINGS
+ #endif
+ #ifndef _CRT_NONSTDC_NO_DEPRECATE
+ #define _CRT_NONSTDC_NO_DEPRECATE
+ #endif
+ #ifndef _CRT_NON_CONFORMING_SWPRINTFS
+ #define _CRT_NON_CONFORMING_SWPRINTFS
+ #endif
+ #if !defined(_MSC_VER) || _MSC_VER > 1700
+ #include <intrin.h> // _BitScanReverse
+ #endif
+#endif
+
+#include <stdlib.h> // stdlib could have min/max
+#include <stdio.h> // need FILE
+#include <string.h> // stb_define_hash needs memcpy/memset
+#include <time.h> // stb_dirtree
+#ifdef __MINGW32__
+ #include <fcntl.h> // O_RDWR
+#endif
+
+#ifdef STB_PERSONAL
+ typedef int Bool;
+ #define False 0
+ #define True 1
+#endif
+
+#ifdef STB_MALLOC_WRAPPER_PAGED
+ #define STB_MALLOC_WRAPPER_DEBUG
+#endif
+#ifdef STB_MALLOC_WRAPPER_DEBUG
+ #define STB_MALLOC_WRAPPER
+#endif
+#ifdef STB_MALLOC_WRAPPER_FASTMALLOC
+ #define STB_FASTMALLOC
+ #define STB_MALLOC_WRAPPER
+#endif
+
+#ifdef STB_FASTMALLOC
+ #ifndef _WIN32
+ #undef STB_FASTMALLOC
+ #endif
+#endif
+
+#ifdef STB_DEFINE
+ #include <assert.h>
+ #include <stdarg.h>
+ #include <stddef.h>
+ #include <ctype.h>
+ #include <math.h>
+ #ifndef _WIN32
+ #include <unistd.h>
+ #else
+ #include <io.h> // _mktemp
+ #include <direct.h> // _rmdir
+ #endif
+ #include <sys/types.h> // stat()/_stat()
+ #include <sys/stat.h> // stat()/_stat()
+#endif
+
+#define stb_min(a,b) ((a) < (b) ? (a) : (b))
+#define stb_max(a,b) ((a) > (b) ? (a) : (b))
+
+#ifndef STB_ONLY
+ #if !defined(__cplusplus) && !defined(min) && !defined(max)
+ #define min(x,y) stb_min(x,y)
+ #define max(x,y) stb_max(x,y)
+ #endif
+
+ #ifndef M_PI
+ #define M_PI 3.14159265358979323846f
+ #endif
+
+ #ifndef TRUE
+ #define TRUE 1
+ #define FALSE 0
+ #endif
+
+ #ifndef deg2rad
+ #define deg2rad(a) ((a)*(M_PI/180))
+ #endif
+ #ifndef rad2deg
+ #define rad2deg(a) ((a)*(180/M_PI))
+ #endif
+
+ #ifndef swap
+ #ifndef __cplusplus
+ #define swap(TYPE,a,b) \
+ do { TYPE stb__t; stb__t = (a); (a) = (b); (b) = stb__t; } while (0)
+ #endif
+ #endif
+
+ typedef unsigned char uint8 ;
+ typedef signed char int8 ;
+ typedef unsigned short uint16;
+ typedef signed short int16;
+ #if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32)
+ typedef unsigned long uint32;
+ typedef signed long int32;
+ #else
+ typedef unsigned int uint32;
+ typedef signed int int32;
+ #endif
+
+ typedef unsigned char uchar ;
+ typedef unsigned short ushort;
+ typedef unsigned int uint ;
+ typedef unsigned long ulong ;
+
+ // produce compile errors if the sizes aren't right
+ typedef char stb__testsize16[sizeof(int16)==2];
+ typedef char stb__testsize32[sizeof(int32)==4];
+#endif
+
+#ifndef STB_TRUE
+ #define STB_TRUE 1
+ #define STB_FALSE 0
+#endif
+
+// if we're STB_ONLY, can't rely on uint32 or even uint, so all the
+// variables we'll use herein need typenames prefixed with 'stb':
+typedef unsigned char stb_uchar;
+typedef unsigned char stb_uint8;
+typedef unsigned int stb_uint;
+typedef unsigned short stb_uint16;
+typedef short stb_int16;
+typedef signed char stb_int8;
+#if defined(STB_USE_LONG_FOR_32_BIT_INT) || defined(STB_LONG32)
+ typedef unsigned long stb_uint32;
+ typedef long stb_int32;
+#else
+ typedef unsigned int stb_uint32;
+ typedef int stb_int32;
+#endif
+typedef char stb__testsize2_16[sizeof(stb_uint16)==2 ? 1 : -1];
+typedef char stb__testsize2_32[sizeof(stb_uint32)==4 ? 1 : -1];
+
+#ifdef _MSC_VER
+ typedef unsigned __int64 stb_uint64;
+ typedef __int64 stb_int64;
+ #define STB_IMM_UINT64(literalui64) (literalui64##ui64)
+ #define STB_IMM_INT64(literali64) (literali64##i64)
+#else
+ // ??
+ typedef unsigned long long stb_uint64;
+ typedef long long stb_int64;
+ #define STB_IMM_UINT64(literalui64) (literalui64##ULL)
+ #define STB_IMM_INT64(literali64) (literali64##LL)
+#endif
+typedef char stb__testsize2_64[sizeof(stb_uint64)==8 ? 1 : -1];
+
+// add platform-specific ways of checking for sizeof(char*) == 8,
+// and make those define STB_PTR64
+#if defined(_WIN64) || defined(__x86_64__) || defined(__ia64__) || defined(__LP64__)
+ #define STB_PTR64
+#endif
+
+#ifdef STB_PTR64
+typedef char stb__testsize2_ptr[sizeof(char *) == 8];
+typedef stb_uint64 stb_uinta;
+typedef stb_int64 stb_inta;
+#else
+typedef char stb__testsize2_ptr[sizeof(char *) == 4];
+typedef stb_uint32 stb_uinta;
+typedef stb_int32 stb_inta;
+#endif
+typedef char stb__testsize2_uinta[sizeof(stb_uinta)==sizeof(char*) ? 1 : -1];
+
+// if so, we should define an int type that is the pointer size. until then,
+// we'll have to make do with this (which is not the same at all!)
+
+typedef union
+{
+ unsigned int i;
+ void * p;
+} stb_uintptr;
+
+
+#ifdef __cplusplus
+ #define STB_EXTERN extern "C"
+#else
+ #define STB_EXTERN extern
+#endif
+
+// check for well-known debug defines
+#if defined(DEBUG) || defined(_DEBUG) || defined(DBG)
+ #ifndef NDEBUG
+ #define STB_DEBUG
+ #endif
+#endif
+
+#ifdef STB_DEBUG
+ #include <assert.h>
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// C library function platform handling
+//
+
+#ifdef STB_DEFINE
+
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+static FILE * stb_p_fopen(const char *filename, const char *mode)
+{
+ FILE *f;
+ if (0 == fopen_s(&f, filename, mode))
+ return f;
+ else
+ return NULL;
+}
+static FILE * stb_p_wfopen(const wchar_t *filename, const wchar_t *mode)
+{
+ FILE *f;
+ if (0 == _wfopen_s(&f, filename, mode))
+ return f;
+ else
+ return NULL;
+}
+static char *stb_p_strcpy_s(char *a, size_t size, const char *b)
+{
+ strcpy_s(a,size,b);
+ return a;
+}
+static char *stb_p_strncpy_s(char *a, size_t size, const char *b, size_t count)
+{
+ strncpy_s(a,size,b,count);
+ return a;
+}
+#define stb_p_mktemp(s) (_mktemp_s(s, strlen(s)+1) == 0)
+#define stb_p_sprintf sprintf_s
+#define stb_p_size(x) ,(x)
+#else
+#define stb_p_fopen fopen
+#define stb_p_wfopen _wfopen
+#define stb_p_strcpy_s(a,s,b) strcpy(a,b)
+#define stb_p_strncpy_s(a,s,b,c) strncpy(a,b,c)
+#define stb_p_mktemp(s) (mktemp(s) != NULL)
+
+#define stb_p_sprintf sprintf
+#define stb_p_size(x)
+#endif
+
+#if defined(_WIN32)
+#define stb_p_vsnprintf _vsnprintf
+#else
+#define stb_p_vsnprintf vsnprintf
+#endif
+#endif // STB_DEFINE
+
+#if defined(_WIN32) && (_MSC_VER >= 1300)
+#define stb_p_stricmp _stricmp
+#define stb_p_strnicmp _strnicmp
+#define stb_p_strdup _strdup
+#else
+#define stb_p_strdup strdup
+#define stb_p_stricmp stricmp
+#define stb_p_strnicmp strnicmp
+#endif
+
+STB_EXTERN void stb_wrapper_malloc(void *newp, size_t sz, char *file, int line);
+STB_EXTERN void stb_wrapper_free(void *oldp, char *file, int line);
+STB_EXTERN void stb_wrapper_realloc(void *oldp, void *newp, size_t sz, char *file, int line);
+STB_EXTERN void stb_wrapper_calloc(size_t num, size_t sz, char *file, int line);
+STB_EXTERN void stb_wrapper_listall(void (*func)(void *ptr, size_t sz, char *file, int line));
+STB_EXTERN void stb_wrapper_dump(char *filename);
+STB_EXTERN size_t stb_wrapper_allocsize(void *oldp);
+STB_EXTERN void stb_wrapper_check(void *oldp);
+
+#ifdef STB_DEFINE
+// this is a special function used inside malloc wrapper
+// to do allocations that aren't tracked (to avoid
+// reentrancy). Of course if someone _else_ wraps realloc,
+// this breaks, but if they're doing that AND the malloc
+// wrapper they need to explicitly check for reentrancy.
+//
+// only define realloc_raw() and we do realloc(NULL,sz)
+// for malloc() and realloc(p,0) for free().
+static void * stb__realloc_raw(void *p, int sz)
+{
+ if (p == NULL) return malloc(sz);
+ if (sz == 0) { free(p); return NULL; }
+ return realloc(p,sz);
+}
+#endif
+
+#ifdef _WIN32
+STB_EXTERN void * stb_smalloc(size_t sz);
+STB_EXTERN void stb_sfree(void *p);
+STB_EXTERN void * stb_srealloc(void *p, size_t sz);
+STB_EXTERN void * stb_scalloc(size_t n, size_t sz);
+STB_EXTERN char * stb_sstrdup(char *s);
+#endif
+
+#ifdef STB_FASTMALLOC
+#define malloc stb_smalloc
+#define free stb_sfree
+#define realloc stb_srealloc
+#define strdup stb_sstrdup
+#define calloc stb_scalloc
+#endif
+
+#ifndef STB_MALLOC_ALLCHECK
+ #define stb__check(p) 1
+#else
+ #ifndef STB_MALLOC_WRAPPER
+ #error STB_MALLOC_ALLCHECK requires STB_MALLOC_WRAPPER
+ #else
+ #define stb__check(p) stb_mcheck(p)
+ #endif
+#endif
+
+#ifdef STB_MALLOC_WRAPPER
+ STB_EXTERN void * stb__malloc(size_t, char *, int);
+ STB_EXTERN void * stb__realloc(void *, size_t, char *, int);
+ STB_EXTERN void * stb__calloc(size_t n, size_t s, char *, int);
+ STB_EXTERN void stb__free(void *, char *file, int);
+ STB_EXTERN char * stb__strdup(char *s, char *file, int);
+ STB_EXTERN void stb_malloc_checkall(void);
+ STB_EXTERN void stb_malloc_check_counter(int init_delay, int rep_delay);
+ #ifndef STB_MALLOC_WRAPPER_DEBUG
+ #define stb_mcheck(p) 1
+ #else
+ STB_EXTERN int stb_mcheck(void *);
+ #endif
+
+
+ #ifdef STB_DEFINE
+
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ #define STB__PAD 32
+ #define STB__BIAS 16
+ #define STB__SIG 0x51b01234
+ #define STB__FIXSIZE(sz) (((sz+3) & ~3) + STB__PAD)
+ #define STB__ptr(x,y) ((char *) (x) + (y))
+ #else
+ #define STB__ptr(x,y) (x)
+ #define STB__FIXSIZE(sz) (sz)
+ #endif
+
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ int stb_mcheck(void *p)
+ {
+ unsigned int sz;
+ if (p == NULL) return 1;
+ p = ((char *) p) - STB__BIAS;
+ sz = * (unsigned int *) p;
+ assert(* (unsigned int *) STB__ptr(p,4) == STB__SIG);
+ assert(* (unsigned int *) STB__ptr(p,8) == STB__SIG);
+ assert(* (unsigned int *) STB__ptr(p,12) == STB__SIG);
+ assert(* (unsigned int *) STB__ptr(p,sz-4) == STB__SIG+1);
+ assert(* (unsigned int *) STB__ptr(p,sz-8) == STB__SIG+1);
+ assert(* (unsigned int *) STB__ptr(p,sz-12) == STB__SIG+1);
+ assert(* (unsigned int *) STB__ptr(p,sz-16) == STB__SIG+1);
+ stb_wrapper_check(STB__ptr(p, STB__BIAS));
+ return 1;
+ }
+
+ static void stb__check2(void *p, size_t sz, char *file, int line)
+ {
+ stb_mcheck(p);
+ }
+
+ void stb_malloc_checkall(void)
+ {
+ stb_wrapper_listall(stb__check2);
+ }
+ #else
+ void stb_malloc_checkall(void) { }
+ #endif
+
+ static int stb__malloc_wait=(1 << 30), stb__malloc_next_wait = (1 << 30), stb__malloc_iter;
+ void stb_malloc_check_counter(int init_delay, int rep_delay)
+ {
+ stb__malloc_wait = init_delay;
+ stb__malloc_next_wait = rep_delay;
+ }
+
+ void stb_mcheck_all(void)
+ {
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ ++stb__malloc_iter;
+ if (--stb__malloc_wait <= 0) {
+ stb_malloc_checkall();
+ stb__malloc_wait = stb__malloc_next_wait;
+ }
+ #endif
+ }
+
+ #ifdef STB_MALLOC_WRAPPER_PAGED
+ #define STB__WINDOWS_PAGE (1 << 12)
+ #ifndef _WINDOWS_
+ STB_EXTERN __declspec(dllimport) void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect);
+ STB_EXTERN __declspec(dllimport) int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype);
+ #endif
+ #endif
+
+ static void *stb__malloc_final(size_t sz)
+ {
+ #ifdef STB_MALLOC_WRAPPER_PAGED
+ size_t aligned = (sz + STB__WINDOWS_PAGE - 1) & ~(STB__WINDOWS_PAGE-1);
+ char *p = VirtualAlloc(NULL, aligned + STB__WINDOWS_PAGE, 0x2000, 0x04); // RESERVE, READWRITE
+ if (p == NULL) return p;
+ VirtualAlloc(p, aligned, 0x1000, 0x04); // COMMIT, READWRITE
+ return p;
+ #else
+ return malloc(sz);
+ #endif
+ }
+
+ static void stb__free_final(void *p)
+ {
+ #ifdef STB_MALLOC_WRAPPER_PAGED
+ VirtualFree(p, 0, 0x8000); // RELEASE
+ #else
+ free(p);
+ #endif
+ }
+
+ int stb__malloc_failure;
+ #ifdef STB_MALLOC_WRAPPER_PAGED
+ static void *stb__realloc_final(void *p, size_t sz, size_t old_sz)
+ {
+ void *q = stb__malloc_final(sz);
+ if (q == NULL)
+ return ++stb__malloc_failure, q;
+ // @TODO: deal with p being smaller!
+ memcpy(q, p, sz < old_sz ? sz : old_sz);
+ stb__free_final(p);
+ return q;
+ }
+ #endif
+
+ void stb__free(void *p, char *file, int line)
+ {
+ stb_mcheck_all();
+ if (!p) return;
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ stb_mcheck(p);
+ #endif
+ stb_wrapper_free(p,file,line);
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ p = STB__ptr(p,-STB__BIAS);
+ * (unsigned int *) STB__ptr(p,0) = 0xdeadbeef;
+ * (unsigned int *) STB__ptr(p,4) = 0xdeadbeef;
+ * (unsigned int *) STB__ptr(p,8) = 0xdeadbeef;
+ * (unsigned int *) STB__ptr(p,12) = 0xdeadbeef;
+ #endif
+ stb__free_final(p);
+ }
+
+ void * stb__malloc(size_t sz, char *file, int line)
+ {
+ void *p;
+ stb_mcheck_all();
+ if (sz == 0) return NULL;
+ p = stb__malloc_final(STB__FIXSIZE(sz));
+ if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz));
+ if (p == NULL) p = stb__malloc_final(STB__FIXSIZE(sz));
+ if (p == NULL) {
+ ++stb__malloc_failure;
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ stb_malloc_checkall();
+ #endif
+ return p;
+ }
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ * (int *) STB__ptr(p,0) = STB__FIXSIZE(sz);
+ * (unsigned int *) STB__ptr(p,4) = STB__SIG;
+ * (unsigned int *) STB__ptr(p,8) = STB__SIG;
+ * (unsigned int *) STB__ptr(p,12) = STB__SIG;
+ * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-4) = STB__SIG+1;
+ * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-8) = STB__SIG+1;
+ * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-12) = STB__SIG+1;
+ * (unsigned int *) STB__ptr(p,STB__FIXSIZE(sz)-16) = STB__SIG+1;
+ p = STB__ptr(p, STB__BIAS);
+ #endif
+ stb_wrapper_malloc(p,sz,file,line);
+ return p;
+ }
+
+ void * stb__realloc(void *p, size_t sz, char *file, int line)
+ {
+ void *q;
+
+ stb_mcheck_all();
+ if (p == NULL) return stb__malloc(sz,file,line);
+ if (sz == 0 ) { stb__free(p,file,line); return NULL; }
+
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ stb_mcheck(p);
+ p = STB__ptr(p,-STB__BIAS);
+ #endif
+ #ifdef STB_MALLOC_WRAPPER_PAGED
+ {
+ size_t n = stb_wrapper_allocsize(STB__ptr(p,STB__BIAS));
+ if (!n)
+ stb_wrapper_check(STB__ptr(p,STB__BIAS));
+ q = stb__realloc_final(p, STB__FIXSIZE(sz), STB__FIXSIZE(n));
+ }
+ #else
+ q = realloc(p, STB__FIXSIZE(sz));
+ #endif
+ if (q == NULL)
+ return ++stb__malloc_failure, q;
+ #ifdef STB_MALLOC_WRAPPER_DEBUG
+ * (int *) STB__ptr(q,0) = STB__FIXSIZE(sz);
+ * (unsigned int *) STB__ptr(q,4) = STB__SIG;
+ * (unsigned int *) STB__ptr(q,8) = STB__SIG;
+ * (unsigned int *) STB__ptr(q,12) = STB__SIG;
+ * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-4) = STB__SIG+1;
+ * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-8) = STB__SIG+1;
+ * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-12) = STB__SIG+1;
+ * (unsigned int *) STB__ptr(q,STB__FIXSIZE(sz)-16) = STB__SIG+1;
+
+ q = STB__ptr(q, STB__BIAS);
+ p = STB__ptr(p, STB__BIAS);
+ #endif
+ stb_wrapper_realloc(p,q,sz,file,line);
+ return q;
+ }
+
+ STB_EXTERN int stb_log2_ceil(size_t);
+ static void *stb__calloc(size_t n, size_t sz, char *file, int line)
+ {
+ void *q;
+ stb_mcheck_all();
+ if (n == 0 || sz == 0) return NULL;
+ if (stb_log2_ceil(n) + stb_log2_ceil(sz) >= 32) return NULL;
+ q = stb__malloc(n*sz, file, line);
+ if (q) memset(q, 0, n*sz);
+ return q;
+ }
+
+ char * stb__strdup(char *s, char *file, int line)
+ {
+ char *p;
+ stb_mcheck_all();
+ p = stb__malloc(strlen(s)+1, file, line);
+ if (!p) return p;
+ stb_p_strcpy_s(p, strlen(s)+1, s);
+ return p;
+ }
+ #endif // STB_DEFINE
+
+ #ifdef STB_FASTMALLOC
+ #undef malloc
+ #undef realloc
+ #undef free
+ #undef strdup
+ #undef calloc
+ #endif
+
+ // include everything that might define these, BEFORE making macros
+ #include <stdlib.h>
+ #include <string.h>
+ #include <malloc.h>
+
+ #define malloc(s) stb__malloc ( s, __FILE__, __LINE__)
+ #define realloc(p,s) stb__realloc(p,s, __FILE__, __LINE__)
+ #define calloc(n,s) stb__calloc (n,s, __FILE__, __LINE__)
+ #define free(p) stb__free (p, __FILE__, __LINE__)
+ #define strdup(p) stb__strdup (p, __FILE__, __LINE__)
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Windows pretty display
+//
+
+STB_EXTERN void stbprint(const char *fmt, ...);
+STB_EXTERN char *stb_sprintf(const char *fmt, ...);
+STB_EXTERN char *stb_mprintf(const char *fmt, ...);
+STB_EXTERN int stb_snprintf(char *s, size_t n, const char *fmt, ...);
+STB_EXTERN int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v);
+
+#ifdef STB_DEFINE
+int stb_vsnprintf(char *s, size_t n, const char *fmt, va_list v)
+{
+ int res;
+ #ifdef _WIN32
+ #ifdef __STDC_WANT_SECURE_LIB__
+ res = _vsnprintf_s(s, n, _TRUNCATE, fmt, v);
+ #else
+ res = stb_p_vsnprintf(s,n,fmt,v);
+ #endif
+ #else
+ res = vsnprintf(s,n,fmt,v);
+ #endif
+ if (n) s[n-1] = 0;
+ // Unix returns length output would require, Windows returns negative when truncated.
+ return (res >= (int) n || res < 0) ? -1 : res;
+}
+
+int stb_snprintf(char *s, size_t n, const char *fmt, ...)
+{
+ int res;
+ va_list v;
+ va_start(v,fmt);
+ res = stb_vsnprintf(s, n, fmt, v);
+ va_end(v);
+ return res;
+}
+
+char *stb_sprintf(const char *fmt, ...)
+{
+ static char buffer[1024];
+ va_list v;
+ va_start(v,fmt);
+ stb_vsnprintf(buffer,1024,fmt,v);
+ va_end(v);
+ return buffer;
+}
+
+char *stb_mprintf(const char *fmt, ...)
+{
+ static char buffer[1024];
+ va_list v;
+ va_start(v,fmt);
+ stb_vsnprintf(buffer,1024,fmt,v);
+ va_end(v);
+ return stb_p_strdup(buffer);
+}
+
+#ifdef _WIN32
+
+#ifndef _WINDOWS_
+STB_EXTERN __declspec(dllimport) int __stdcall WriteConsoleA(void *, const void *, unsigned int, unsigned int *, void *);
+STB_EXTERN __declspec(dllimport) void * __stdcall GetStdHandle(unsigned int);
+STB_EXTERN __declspec(dllimport) int __stdcall SetConsoleTextAttribute(void *, unsigned short);
+#endif
+
+static void stb__print_one(void *handle, char *s, ptrdiff_t len)
+{
+ if (len)
+ if (0==WriteConsoleA(handle, s, (unsigned) len, NULL,NULL))
+ // if it fails, maybe redirected, so output normally...
+ // but it's supriously reporting failure now on Win7 and later
+ {}//fwrite(s, 1, (unsigned) len, stdout);
+}
+
+static void stb__print(char *s)
+{
+ void *handle = GetStdHandle((unsigned int) -11); // STD_OUTPUT_HANDLE
+ int pad=0; // number of padding characters to add
+
+ char *t = s;
+ while (*s) {
+ int lpad;
+ while (*s && *s != '{') {
+ if (pad) {
+ if (*s == '\r' || *s == '\n')
+ pad = 0;
+ else if (s[0] == ' ' && s[1] == ' ') {
+ stb__print_one(handle, t, s-t);
+ t = s;
+ while (pad) {
+ stb__print_one(handle, t, 1);
+ --pad;
+ }
+ }
+ }
+ ++s;
+ }
+ if (!*s) break;
+ stb__print_one(handle, t, s-t);
+ if (s[1] == '{') {
+ ++s;
+ continue;
+ }
+
+ if (s[1] == '#') {
+ t = s+3;
+ if (isxdigit(s[2]))
+ if (isdigit(s[2]))
+ SetConsoleTextAttribute(handle, s[2] - '0');
+ else
+ SetConsoleTextAttribute(handle, tolower(s[2]) - 'a' + 10);
+ else {
+ SetConsoleTextAttribute(handle, 0x0f);
+ t=s+2;
+ }
+ } else if (s[1] == '!') {
+ SetConsoleTextAttribute(handle, 0x0c);
+ t = s+2;
+ } else if (s[1] == '@') {
+ SetConsoleTextAttribute(handle, 0x09);
+ t = s+2;
+ } else if (s[1] == '$') {
+ SetConsoleTextAttribute(handle, 0x0a);
+ t = s+2;
+ } else {
+ SetConsoleTextAttribute(handle, 0x08); // 0,7,8,15 => shades of grey
+ t = s+1;
+ }
+
+ lpad = (int) (t-s);
+ s = t;
+ while (*s && *s != '}') ++s;
+ if (!*s) break;
+ stb__print_one(handle, t, s-t);
+ if (s[1] == '}') {
+ t = s+2;
+ } else {
+ pad += 1+lpad;
+ t = s+1;
+ }
+ s=t;
+ SetConsoleTextAttribute(handle, 0x07);
+ }
+ stb__print_one(handle, t, s-t);
+ SetConsoleTextAttribute(handle, 0x07);
+}
+
+void stbprint(const char *fmt, ...)
+{
+ int res;
+ char buffer[1024];
+ char *tbuf = buffer;
+ va_list v;
+
+ va_start(v,fmt);
+ res = stb_vsnprintf(buffer, sizeof(buffer), fmt, v);
+ va_end(v);
+
+ if (res < 0) {
+ tbuf = (char *) malloc(16384);
+ va_start(v,fmt);
+ res = stb_vsnprintf(tbuf,16384, fmt, v);
+ va_end(v);
+ tbuf[16383] = 0;
+ }
+
+ stb__print(tbuf);
+
+ if (tbuf != buffer)
+ free(tbuf);
+}
+
+#else // _WIN32
+void stbprint(const char *fmt, ...)
+{
+ va_list v;
+ va_start(v,fmt);
+ vprintf(fmt,v);
+ va_end(v);
+}
+#endif // _WIN32
+#endif // STB_DEFINE
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Windows UTF8 filename handling
+//
+// Windows stupidly treats 8-bit filenames as some dopey code page,
+// rather than utf-8. If we want to use utf8 filenames, we have to
+// convert them to WCHAR explicitly and call WCHAR versions of the
+// file functions. So, ok, we do.
+
+
+#ifdef _WIN32
+ #define stb__fopen(x,y) stb_p_wfopen((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y))
+ #define stb__windows(x,y) x
+#else
+ #define stb__fopen(x,y) stb_p_fopen(x,y)
+ #define stb__windows(x,y) y
+#endif
+
+
+typedef unsigned short stb__wchar;
+
+STB_EXTERN stb__wchar * stb_from_utf8(stb__wchar *buffer, const char *str, int n);
+STB_EXTERN char * stb_to_utf8 (char *buffer, const stb__wchar *str, int n);
+
+STB_EXTERN stb__wchar *stb__from_utf8(const char *str);
+STB_EXTERN stb__wchar *stb__from_utf8_alt(const char *str);
+STB_EXTERN char *stb__to_utf8(const stb__wchar *str);
+
+
+#ifdef STB_DEFINE
+stb__wchar * stb_from_utf8(stb__wchar *buffer, const char *ostr, int n)
+{
+ unsigned char *str = (unsigned char *) ostr;
+ stb_uint32 c;
+ int i=0;
+ --n;
+ while (*str) {
+ if (i >= n)
+ return NULL;
+ if (!(*str & 0x80))
+ buffer[i++] = *str++;
+ else if ((*str & 0xe0) == 0xc0) {
+ if (*str < 0xc2) return NULL;
+ c = (*str++ & 0x1f) << 6;
+ if ((*str & 0xc0) != 0x80) return NULL;
+ buffer[i++] = c + (*str++ & 0x3f);
+ } else if ((*str & 0xf0) == 0xe0) {
+ if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL;
+ if (*str == 0xed && str[1] > 0x9f) return NULL; // str[1] < 0x80 is checked below
+ c = (*str++ & 0x0f) << 12;
+ if ((*str & 0xc0) != 0x80) return NULL;
+ c += (*str++ & 0x3f) << 6;
+ if ((*str & 0xc0) != 0x80) return NULL;
+ buffer[i++] = c + (*str++ & 0x3f);
+ } else if ((*str & 0xf8) == 0xf0) {
+ if (*str > 0xf4) return NULL;
+ if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL;
+ if (*str == 0xf4 && str[1] > 0x8f) return NULL; // str[1] < 0x80 is checked below
+ c = (*str++ & 0x07) << 18;
+ if ((*str & 0xc0) != 0x80) return NULL;
+ c += (*str++ & 0x3f) << 12;
+ if ((*str & 0xc0) != 0x80) return NULL;
+ c += (*str++ & 0x3f) << 6;
+ if ((*str & 0xc0) != 0x80) return NULL;
+ c += (*str++ & 0x3f);
+ // utf-8 encodings of values used in surrogate pairs are invalid
+ if ((c & 0xFFFFF800) == 0xD800) return NULL;
+ if (c >= 0x10000) {
+ c -= 0x10000;
+ if (i + 2 > n) return NULL;
+ buffer[i++] = 0xD800 | (0x3ff & (c >> 10));
+ buffer[i++] = 0xDC00 | (0x3ff & (c ));
+ }
+ } else
+ return NULL;
+ }
+ buffer[i] = 0;
+ return buffer;
+}
+
+char * stb_to_utf8(char *buffer, const stb__wchar *str, int n)
+{
+ int i=0;
+ --n;
+ while (*str) {
+ if (*str < 0x80) {
+ if (i+1 > n) return NULL;
+ buffer[i++] = (char) *str++;
+ } else if (*str < 0x800) {
+ if (i+2 > n) return NULL;
+ buffer[i++] = 0xc0 + (*str >> 6);
+ buffer[i++] = 0x80 + (*str & 0x3f);
+ str += 1;
+ } else if (*str >= 0xd800 && *str < 0xdc00) {
+ stb_uint32 c;
+ if (i+4 > n) return NULL;
+ c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000;
+ buffer[i++] = 0xf0 + (c >> 18);
+ buffer[i++] = 0x80 + ((c >> 12) & 0x3f);
+ buffer[i++] = 0x80 + ((c >> 6) & 0x3f);
+ buffer[i++] = 0x80 + ((c ) & 0x3f);
+ str += 2;
+ } else if (*str >= 0xdc00 && *str < 0xe000) {
+ return NULL;
+ } else {
+ if (i+3 > n) return NULL;
+ buffer[i++] = 0xe0 + (*str >> 12);
+ buffer[i++] = 0x80 + ((*str >> 6) & 0x3f);
+ buffer[i++] = 0x80 + ((*str ) & 0x3f);
+ str += 1;
+ }
+ }
+ buffer[i] = 0;
+ return buffer;
+}
+
+stb__wchar *stb__from_utf8(const char *str)
+{
+ static stb__wchar buffer[4096];
+ return stb_from_utf8(buffer, str, 4096);
+}
+
+stb__wchar *stb__from_utf8_alt(const char *str)
+{
+ static stb__wchar buffer[4096];
+ return stb_from_utf8(buffer, str, 4096);
+}
+
+char *stb__to_utf8(const stb__wchar *str)
+{
+ static char buffer[4096];
+ return stb_to_utf8(buffer, str, 4096);
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Miscellany
+//
+
+STB_EXTERN void stb_fatal(const char *fmt, ...);
+STB_EXTERN void stb_(char *fmt, ...);
+STB_EXTERN void stb_append_to_file(char *file, char *fmt, ...);
+STB_EXTERN void stb_log(int active);
+STB_EXTERN void stb_log_fileline(int active);
+STB_EXTERN void stb_log_name(char *filename);
+
+STB_EXTERN void stb_swap(void *p, void *q, size_t sz);
+STB_EXTERN void *stb_copy(void *p, size_t sz);
+STB_EXTERN void stb_pointer_array_free(void *p, int len);
+STB_EXTERN void **stb_array_block_alloc(int count, int blocksize);
+
+#define stb_arrcount(x) (sizeof(x)/sizeof((x)[0]))
+
+
+STB_EXTERN int stb__record_fileline(const char *f, int n);
+
+#ifdef STB_DEFINE
+
+static char *stb__file;
+static int stb__line;
+
+int stb__record_fileline(const char *f, int n)
+{
+ stb__file = (char*) f;
+ stb__line = n;
+ return 0;
+}
+
+void stb_fatal(const char *s, ...)
+{
+ va_list a;
+ if (stb__file)
+ fprintf(stderr, "[%s:%d] ", stb__file, stb__line);
+ va_start(a,s);
+ fputs("Fatal error: ", stderr);
+ vfprintf(stderr, s, a);
+ va_end(a);
+ fputs("\n", stderr);
+ #ifdef STB_DEBUG
+ #ifdef _MSC_VER
+ #ifndef STB_PTR64
+ __asm int 3; // trap to debugger!
+ #else
+ __debugbreak();
+ #endif
+ #else
+ __builtin_trap();
+ #endif
+ #endif
+ exit(1);
+}
+
+static int stb__log_active=1, stb__log_fileline=1;
+
+void stb_log(int active)
+{
+ stb__log_active = active;
+}
+
+void stb_log_fileline(int active)
+{
+ stb__log_fileline = active;
+}
+
+#ifdef STB_NO_STB_STRINGS
+const char *stb__log_filename = "temp.log";
+#else
+const char *stb__log_filename = "stb.log";
+#endif
+
+void stb_log_name(char *s)
+{
+ stb__log_filename = s;
+}
+
+void stb_(char *s, ...)
+{
+ if (stb__log_active) {
+ FILE *f = stb_p_fopen(stb__log_filename, "a");
+ if (f) {
+ va_list a;
+ if (stb__log_fileline && stb__file)
+ fprintf(f, "[%s:%4d] ", stb__file, stb__line);
+ va_start(a,s);
+ vfprintf(f, s, a);
+ va_end(a);
+ fputs("\n", f);
+ fclose(f);
+ }
+ }
+}
+
+void stb_append_to_file(char *filename, char *s, ...)
+{
+ FILE *f = stb_p_fopen(filename, "a");
+ if (f) {
+ va_list a;
+ va_start(a,s);
+ vfprintf(f, s, a);
+ va_end(a);
+ fputs("\n", f);
+ fclose(f);
+ }
+}
+
+
+typedef struct { char d[4]; } stb__4;
+typedef struct { char d[8]; } stb__8;
+
+// optimize the small cases, though you shouldn't be calling this for those!
+void stb_swap(void *p, void *q, size_t sz)
+{
+ char buffer[256];
+ if (p == q) return;
+ if (sz == 4) {
+ stb__4 temp = * ( stb__4 *) p;
+ * (stb__4 *) p = * ( stb__4 *) q;
+ * (stb__4 *) q = temp;
+ return;
+ } else if (sz == 8) {
+ stb__8 temp = * ( stb__8 *) p;
+ * (stb__8 *) p = * ( stb__8 *) q;
+ * (stb__8 *) q = temp;
+ return;
+ }
+
+ while (sz > sizeof(buffer)) {
+ stb_swap(p, q, sizeof(buffer));
+ p = (char *) p + sizeof(buffer);
+ q = (char *) q + sizeof(buffer);
+ sz -= sizeof(buffer);
+ }
+
+ memcpy(buffer, p , sz);
+ memcpy(p , q , sz);
+ memcpy(q , buffer, sz);
+}
+
+void *stb_copy(void *p, size_t sz)
+{
+ void *q = malloc(sz);
+ memcpy(q, p, sz);
+ return q;
+}
+
+void stb_pointer_array_free(void *q, int len)
+{
+ void **p = (void **) q;
+ int i;
+ for (i=0; i < len; ++i)
+ free(p[i]);
+}
+
+void **stb_array_block_alloc(int count, int blocksize)
+{
+ int i;
+ char *p = (char *) malloc(sizeof(void *) * count + count * blocksize);
+ void **q;
+ if (p == NULL) return NULL;
+ q = (void **) p;
+ p += sizeof(void *) * count;
+ for (i=0; i < count; ++i)
+ q[i] = p + i * blocksize;
+ return q;
+}
+#endif
+
+#ifdef STB_DEBUG
+ // tricky hack to allow recording FILE,LINE even in varargs functions
+ #define STB__RECORD_FILE(x) (stb__record_fileline(__FILE__, __LINE__),(x))
+ #define stb_log STB__RECORD_FILE(stb_log)
+ #define stb_ STB__RECORD_FILE(stb_)
+ #ifndef STB_FATAL_CLEAN
+ #define stb_fatal STB__RECORD_FILE(stb_fatal)
+ #endif
+ #define STB__DEBUG(x) x
+#else
+ #define STB__DEBUG(x)
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_temp
+//
+
+#define stb_temp(block, sz) stb__temp(block, sizeof(block), (sz))
+
+STB_EXTERN void * stb__temp(void *b, int b_sz, int want_sz);
+STB_EXTERN void stb_tempfree(void *block, void *ptr);
+
+#ifdef STB_DEFINE
+
+void * stb__temp(void *b, int b_sz, int want_sz)
+{
+ if (b_sz >= want_sz)
+ return b;
+ else
+ return malloc(want_sz);
+}
+
+void stb_tempfree(void *b, void *p)
+{
+ if (p != b)
+ free(p);
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// math/sampling operations
+//
+
+
+#define stb_lerp(t,a,b) ( (a) + (t) * (float) ((b)-(a)) )
+#define stb_unlerp(t,a,b) ( ((t) - (a)) / (float) ((b) - (a)) )
+
+#define stb_clamp(x,xmin,xmax) ((x) < (xmin) ? (xmin) : (x) > (xmax) ? (xmax) : (x))
+
+STB_EXTERN void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize);
+STB_EXTERN int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis);
+STB_EXTERN void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt);
+
+STB_EXTERN int stb_float_eq(float x, float y, float delta, int max_ulps);
+STB_EXTERN int stb_is_prime(unsigned int m);
+STB_EXTERN unsigned int stb_power_of_two_nearest_prime(int n);
+
+STB_EXTERN float stb_smoothstep(float t);
+STB_EXTERN float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3);
+
+STB_EXTERN double stb_linear_remap(double x, double a, double b,
+ double c, double d);
+
+#ifdef STB_DEFINE
+float stb_smoothstep(float t)
+{
+ return (3 - 2*t)*(t*t);
+}
+
+float stb_cubic_bezier_1d(float t, float p0, float p1, float p2, float p3)
+{
+ float it = 1-t;
+ return it*it*it*p0 + 3*it*it*t*p1 + 3*it*t*t*p2 + t*t*t*p3;
+}
+
+void stb_newell_normal(float *normal, int num_vert, float **vert, int normalize)
+{
+ int i,j;
+ float p;
+ normal[0] = normal[1] = normal[2] = 0;
+ for (i=num_vert-1,j=0; j < num_vert; i=j++) {
+ float *u = vert[i];
+ float *v = vert[j];
+ normal[0] += (u[1] - v[1]) * (u[2] + v[2]);
+ normal[1] += (u[2] - v[2]) * (u[0] + v[0]);
+ normal[2] += (u[0] - v[0]) * (u[1] + v[1]);
+ }
+ if (normalize) {
+ p = normal[0]*normal[0] + normal[1]*normal[1] + normal[2]*normal[2];
+ p = (float) (1.0 / sqrt(p));
+ normal[0] *= p;
+ normal[1] *= p;
+ normal[2] *= p;
+ }
+}
+
+int stb_box_face_vertex_axis_side(int face_number, int vertex_number, int axis)
+{
+ static int box_vertices[6][4][3] =
+ {
+ { { 1,1,1 }, { 1,0,1 }, { 1,0,0 }, { 1,1,0 } },
+ { { 0,0,0 }, { 0,0,1 }, { 0,1,1 }, { 0,1,0 } },
+ { { 0,0,0 }, { 0,1,0 }, { 1,1,0 }, { 1,0,0 } },
+ { { 0,0,0 }, { 1,0,0 }, { 1,0,1 }, { 0,0,1 } },
+ { { 1,1,1 }, { 0,1,1 }, { 0,0,1 }, { 1,0,1 } },
+ { { 1,1,1 }, { 1,1,0 }, { 0,1,0 }, { 0,1,1 } },
+ };
+ assert(face_number >= 0 && face_number < 6);
+ assert(vertex_number >= 0 && vertex_number < 4);
+ assert(axis >= 0 && axis < 3);
+ return box_vertices[face_number][vertex_number][axis];
+}
+
+void stb_linear_controller(float *curpos, float target_pos, float acc, float deacc, float dt)
+{
+ float sign = 1, p, cp = *curpos;
+ if (cp == target_pos) return;
+ if (target_pos < cp) {
+ target_pos = -target_pos;
+ cp = -cp;
+ sign = -1;
+ }
+ // first decelerate
+ if (cp < 0) {
+ p = cp + deacc * dt;
+ if (p > 0) {
+ p = 0;
+ dt = dt - cp / deacc;
+ if (dt < 0) dt = 0;
+ } else {
+ dt = 0;
+ }
+ cp = p;
+ }
+ // now accelerate
+ p = cp + acc*dt;
+ if (p > target_pos) p = target_pos;
+ *curpos = p * sign;
+ // @TODO: testing
+}
+
+float stb_quadratic_controller(float target_pos, float curpos, float maxvel, float maxacc, float dt, float *curvel)
+{
+ return 0; // @TODO
+}
+
+int stb_float_eq(float x, float y, float delta, int max_ulps)
+{
+ if (fabs(x-y) <= delta) return 1;
+ if (abs(*(int *)&x - *(int *)&y) <= max_ulps) return 1;
+ return 0;
+}
+
+int stb_is_prime(unsigned int m)
+{
+ unsigned int i,j;
+ if (m < 2) return 0;
+ if (m == 2) return 1;
+ if (!(m & 1)) return 0;
+ if (m % 3 == 0) return (m == 3);
+ for (i=5; (j=i*i), j <= m && j > i; i += 6) {
+ if (m % i == 0) return 0;
+ if (m % (i+2) == 0) return 0;
+ }
+ return 1;
+}
+
+unsigned int stb_power_of_two_nearest_prime(int n)
+{
+ static signed char tab[32] = { 0,0,0,0,1,0,-1,0,1,-1,-1,3,-1,0,-1,2,1,
+ 0,2,0,-1,-4,-1,5,-1,18,-2,15,2,-1,2,0 };
+ if (!tab[0]) {
+ int i;
+ for (i=0; i < 32; ++i)
+ tab[i] = (1 << i) + 2*tab[i] - 1;
+ tab[1] = 2;
+ tab[0] = 1;
+ }
+ if (n >= 32) return 0xfffffffb;
+ return tab[n];
+}
+
+double stb_linear_remap(double x, double x_min, double x_max,
+ double out_min, double out_max)
+{
+ return stb_lerp(stb_unlerp(x,x_min,x_max),out_min,out_max);
+}
+#endif
+
+// create a macro so it's faster, but you can get at the function pointer
+#define stb_linear_remap(t,a,b,c,d) stb_lerp(stb_unlerp(t,a,b),c,d)
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// bit operations
+//
+
+#define stb_big32(c) (((c)[0]<<24) + (c)[1]*65536 + (c)[2]*256 + (c)[3])
+#define stb_little32(c) (((c)[3]<<24) + (c)[2]*65536 + (c)[1]*256 + (c)[0])
+#define stb_big16(c) ((c)[0]*256 + (c)[1])
+#define stb_little16(c) ((c)[1]*256 + (c)[0])
+
+STB_EXTERN int stb_bitcount(unsigned int a);
+STB_EXTERN unsigned int stb_bitreverse8(unsigned char n);
+STB_EXTERN unsigned int stb_bitreverse(unsigned int n);
+
+STB_EXTERN int stb_is_pow2(size_t);
+STB_EXTERN int stb_log2_ceil(size_t);
+STB_EXTERN int stb_log2_floor(size_t);
+
+STB_EXTERN int stb_lowbit8(unsigned int n);
+STB_EXTERN int stb_highbit8(unsigned int n);
+
+#ifdef STB_DEFINE
+int stb_bitcount(unsigned int a)
+{
+ a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2
+ a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4
+ a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits
+ a = (a + (a >> 8)); // max 16 per 8 bits
+ a = (a + (a >> 16)); // max 32 per 8 bits
+ return a & 0xff;
+}
+
+unsigned int stb_bitreverse8(unsigned char n)
+{
+ n = ((n & 0xAA) >> 1) + ((n & 0x55) << 1);
+ n = ((n & 0xCC) >> 2) + ((n & 0x33) << 2);
+ return (unsigned char) ((n >> 4) + (n << 4));
+}
+
+unsigned int stb_bitreverse(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);
+}
+
+int stb_is_pow2(size_t n)
+{
+ return (n & (n-1)) == 0;
+}
+
+// tricky use of 4-bit table to identify 5 bit positions (note the '-1')
+// 3-bit table would require another tree level; 5-bit table wouldn't save one
+#if defined(_WIN32) && !defined(__MINGW32__)
+#pragma warning(push)
+#pragma warning(disable: 4035) // disable warning about no return value
+int stb_log2_floor(size_t n)
+{
+ #if _MSC_VER > 1700
+ unsigned long i;
+ #ifdef STB_PTR64
+ _BitScanReverse64(&i, n);
+ #else
+ _BitScanReverse(&i, n);
+ #endif
+ return i != 0 ? i : -1;
+ #else
+ __asm {
+ bsr eax,n
+ jnz done
+ mov eax,-1
+ }
+ done:;
+ #endif
+}
+#pragma warning(pop)
+#else
+int stb_log2_floor(size_t n)
+{
+ static signed char log2_4[16] = { -1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3 };
+
+#ifdef STB_PTR64
+ if (n >= ((size_t) 1u << 32))
+ return stb_log2_floor(n >> 32);
+#endif
+
+ // 2 compares if n < 16, 3 compares otherwise
+ if (n < (1U << 14))
+ if (n < (1U << 4)) return 0 + log2_4[n ];
+ else if (n < (1U << 9)) return 5 + log2_4[n >> 5];
+ else return 10 + log2_4[n >> 10];
+ else if (n < (1U << 24))
+ if (n < (1U << 19)) return 15 + log2_4[n >> 15];
+ else return 20 + log2_4[n >> 20];
+ else if (n < (1U << 29)) return 25 + log2_4[n >> 25];
+ else return 30 + log2_4[n >> 30];
+}
+#endif
+
+// define ceil from floor
+int stb_log2_ceil(size_t n)
+{
+ if (stb_is_pow2(n)) return stb_log2_floor(n);
+ else return 1 + stb_log2_floor(n);
+}
+
+int stb_highbit8(unsigned int n)
+{
+ return stb_log2_ceil(n&255);
+}
+
+int stb_lowbit8(unsigned int n)
+{
+ static signed char lowbit4[16] = { -1,0,1,0, 2,0,1,0, 3,0,1,0, 2,0,1,0 };
+ int k = lowbit4[n & 15];
+ if (k >= 0) return k;
+ k = lowbit4[(n >> 4) & 15];
+ if (k >= 0) return k+4;
+ return k;
+}
+#endif
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// qsort Compare Routines
+//
+
+#ifdef _WIN32
+ #define stb_stricmp(a,b) stb_p_stricmp(a,b)
+ #define stb_strnicmp(a,b,n) stb_p_strnicmp(a,b,n)
+#else
+ #define stb_stricmp(a,b) strcasecmp(a,b)
+ #define stb_strnicmp(a,b,n) strncasecmp(a,b,n)
+#endif
+
+
+STB_EXTERN int (*stb_intcmp(int offset))(const void *a, const void *b);
+STB_EXTERN int (*stb_intcmprev(int offset))(const void *a, const void *b);
+STB_EXTERN int (*stb_qsort_strcmp(int offset))(const void *a, const void *b);
+STB_EXTERN int (*stb_qsort_stricmp(int offset))(const void *a, const void *b);
+STB_EXTERN int (*stb_floatcmp(int offset))(const void *a, const void *b);
+STB_EXTERN int (*stb_doublecmp(int offset))(const void *a, const void *b);
+STB_EXTERN int (*stb_charcmp(int offset))(const void *a, const void *b);
+
+#ifdef STB_DEFINE
+static int stb__intcmpoffset, stb__ucharcmpoffset, stb__strcmpoffset;
+static int stb__floatcmpoffset, stb__doublecmpoffset;
+static int stb__memcmpoffset, stb__memcmpsize;
+
+int stb__intcmp(const void *a, const void *b)
+{
+ const int p = *(const int *) ((const char *) a + stb__intcmpoffset);
+ const int q = *(const int *) ((const char *) b + stb__intcmpoffset);
+ return p < q ? -1 : p > q;
+}
+
+int stb__intcmprev(const void *a, const void *b)
+{
+ const int p = *(const int *) ((const char *) a + stb__intcmpoffset);
+ const int q = *(const int *) ((const char *) b + stb__intcmpoffset);
+ return q < p ? -1 : q > p;
+}
+
+int stb__ucharcmp(const void *a, const void *b)
+{
+ const int p = *(const unsigned char *) ((const char *) a + stb__ucharcmpoffset);
+ const int q = *(const unsigned char *) ((const char *) b + stb__ucharcmpoffset);
+ return p < q ? -1 : p > q;
+}
+
+int stb__floatcmp(const void *a, const void *b)
+{
+ const float p = *(const float *) ((const char *) a + stb__floatcmpoffset);
+ const float q = *(const float *) ((const char *) b + stb__floatcmpoffset);
+ return p < q ? -1 : p > q;
+}
+
+int stb__doublecmp(const void *a, const void *b)
+{
+ const double p = *(const double *) ((const char *) a + stb__doublecmpoffset);
+ const double q = *(const double *) ((const char *) b + stb__doublecmpoffset);
+ return p < q ? -1 : p > q;
+}
+
+int stb__qsort_strcmp(const void *a, const void *b)
+{
+ const char *p = *(const char **) ((const char *) a + stb__strcmpoffset);
+ const char *q = *(const char **) ((const char *) b + stb__strcmpoffset);
+ return strcmp(p,q);
+}
+
+int stb__qsort_stricmp(const void *a, const void *b)
+{
+ const char *p = *(const char **) ((const char *) a + stb__strcmpoffset);
+ const char *q = *(const char **) ((const char *) b + stb__strcmpoffset);
+ return stb_stricmp(p,q);
+}
+
+int stb__memcmp(const void *a, const void *b)
+{
+ return memcmp((char *) a + stb__memcmpoffset, (char *) b + stb__memcmpoffset, stb__memcmpsize);
+}
+
+int (*stb_intcmp(int offset))(const void *, const void *)
+{
+ stb__intcmpoffset = offset;
+ return &stb__intcmp;
+}
+
+int (*stb_intcmprev(int offset))(const void *, const void *)
+{
+ stb__intcmpoffset = offset;
+ return &stb__intcmprev;
+}
+
+int (*stb_ucharcmp(int offset))(const void *, const void *)
+{
+ stb__ucharcmpoffset = offset;
+ return &stb__ucharcmp;
+}
+
+int (*stb_qsort_strcmp(int offset))(const void *, const void *)
+{
+ stb__strcmpoffset = offset;
+ return &stb__qsort_strcmp;
+}
+
+int (*stb_qsort_stricmp(int offset))(const void *, const void *)
+{
+ stb__strcmpoffset = offset;
+ return &stb__qsort_stricmp;
+}
+
+int (*stb_floatcmp(int offset))(const void *, const void *)
+{
+ stb__floatcmpoffset = offset;
+ return &stb__floatcmp;
+}
+
+int (*stb_doublecmp(int offset))(const void *, const void *)
+{
+ stb__doublecmpoffset = offset;
+ return &stb__doublecmp;
+}
+
+int (*stb_memcmp(int offset, int size))(const void *, const void *)
+{
+ stb__memcmpoffset = offset;
+ stb__memcmpsize = size;
+ return &stb__memcmp;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Binary Search Toolkit
+//
+
+typedef struct
+{
+ int minval, maxval, guess;
+ int mode, step;
+} stb_search;
+
+STB_EXTERN int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest);
+STB_EXTERN int stb_search_open(stb_search *s, int minv, int find_smallest);
+STB_EXTERN int stb_probe(stb_search *s, int compare, int *result); // return 0 when done
+
+#ifdef STB_DEFINE
+enum
+{
+ STB_probe_binary_smallest,
+ STB_probe_binary_largest,
+ STB_probe_open_smallest,
+ STB_probe_open_largest,
+};
+
+static int stb_probe_guess(stb_search *s, int *result)
+{
+ switch(s->mode) {
+ case STB_probe_binary_largest:
+ if (s->minval == s->maxval) {
+ *result = s->minval;
+ return 0;
+ }
+ assert(s->minval < s->maxval);
+ // if a < b, then a < p <= b
+ s->guess = s->minval + (((unsigned) s->maxval - s->minval + 1) >> 1);
+ break;
+
+ case STB_probe_binary_smallest:
+ if (s->minval == s->maxval) {
+ *result = s->minval;
+ return 0;
+ }
+ assert(s->minval < s->maxval);
+ // if a < b, then a <= p < b
+ s->guess = s->minval + (((unsigned) s->maxval - s->minval) >> 1);
+ break;
+ case STB_probe_open_smallest:
+ case STB_probe_open_largest:
+ s->guess = s->maxval; // guess the current maxval
+ break;
+ }
+ *result = s->guess;
+ return 1;
+}
+
+int stb_probe(stb_search *s, int compare, int *result)
+{
+ switch(s->mode) {
+ case STB_probe_open_smallest:
+ case STB_probe_open_largest: {
+ if (compare <= 0) {
+ // then it lies within minval & maxval
+ if (s->mode == STB_probe_open_smallest)
+ s->mode = STB_probe_binary_smallest;
+ else
+ s->mode = STB_probe_binary_largest;
+ } else {
+ // otherwise, we need to probe larger
+ s->minval = s->maxval + 1;
+ s->maxval = s->minval + s->step;
+ s->step += s->step;
+ }
+ break;
+ }
+ case STB_probe_binary_smallest: {
+ // if compare < 0, then s->minval <= a < p
+ // if compare = 0, then s->minval <= a <= p
+ // if compare > 0, then p < a <= s->maxval
+ if (compare <= 0)
+ s->maxval = s->guess;
+ else
+ s->minval = s->guess+1;
+ break;
+ }
+ case STB_probe_binary_largest: {
+ // if compare < 0, then s->minval <= a < p
+ // if compare = 0, then p <= a <= s->maxval
+ // if compare > 0, then p < a <= s->maxval
+ if (compare < 0)
+ s->maxval = s->guess-1;
+ else
+ s->minval = s->guess;
+ break;
+ }
+ }
+ return stb_probe_guess(s, result);
+}
+
+int stb_search_binary(stb_search *s, int minv, int maxv, int find_smallest)
+{
+ int r;
+ if (maxv < minv) return minv-1;
+ s->minval = minv;
+ s->maxval = maxv;
+ s->mode = find_smallest ? STB_probe_binary_smallest : STB_probe_binary_largest;
+ stb_probe_guess(s, &r);
+ return r;
+}
+
+int stb_search_open(stb_search *s, int minv, int find_smallest)
+{
+ int r;
+ s->step = 4;
+ s->minval = minv;
+ s->maxval = minv+s->step;
+ s->mode = find_smallest ? STB_probe_open_smallest : STB_probe_open_largest;
+ stb_probe_guess(s, &r);
+ return r;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// String Processing
+//
+
+#define stb_prefixi(s,t) (0==stb_strnicmp((s),(t),strlen(t)))
+
+enum stb_splitpath_flag
+{
+ STB_PATH = 1,
+ STB_FILE = 2,
+ STB_EXT = 4,
+ STB_PATH_FILE = STB_PATH + STB_FILE,
+ STB_FILE_EXT = STB_FILE + STB_EXT,
+ STB_EXT_NO_PERIOD = 8,
+};
+
+STB_EXTERN char * stb_skipwhite(char *s);
+STB_EXTERN char * stb_trimwhite(char *s);
+STB_EXTERN char * stb_skipnewline(char *s);
+STB_EXTERN char * stb_strncpy(char *s, char *t, int n);
+STB_EXTERN char * stb_substr(char *t, int n);
+STB_EXTERN char * stb_duplower(char *s);
+STB_EXTERN void stb_tolower (char *s);
+STB_EXTERN char * stb_strchr2 (char *s, char p1, char p2);
+STB_EXTERN char * stb_strrchr2(char *s, char p1, char p2);
+STB_EXTERN char * stb_strtok(char *output, char *src, char *delimit);
+STB_EXTERN char * stb_strtok_keep(char *output, char *src, char *delimit);
+STB_EXTERN char * stb_strtok_invert(char *output, char *src, char *allowed);
+STB_EXTERN char * stb_dupreplace(char *s, char *find, char *replace);
+STB_EXTERN void stb_replaceinplace(char *s, char *find, char *replace);
+STB_EXTERN char * stb_splitpath(char *output, char *src, int flag);
+STB_EXTERN char * stb_splitpathdup(char *src, int flag);
+STB_EXTERN char * stb_replacedir(char *output, char *src, char *dir);
+STB_EXTERN char * stb_replaceext(char *output, char *src, char *ext);
+STB_EXTERN void stb_fixpath(char *path);
+STB_EXTERN char * stb_shorten_path_readable(char *path, int max_len);
+STB_EXTERN int stb_suffix (char *s, char *t);
+STB_EXTERN int stb_suffixi(char *s, char *t);
+STB_EXTERN int stb_prefix (char *s, char *t);
+STB_EXTERN char * stb_strichr(char *s, char t);
+STB_EXTERN char * stb_stristr(char *s, char *t);
+STB_EXTERN int stb_prefix_count(char *s, char *t);
+STB_EXTERN const char * stb_plural(int n); // "s" or ""
+STB_EXTERN size_t stb_strscpy(char *d, const char *s, size_t n);
+
+STB_EXTERN char **stb_tokens(char *src, char *delimit, int *count);
+STB_EXTERN char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out);
+STB_EXTERN char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out);
+STB_EXTERN char **stb_tokens_allowempty(char *src, char *delimit, int *count);
+STB_EXTERN char **stb_tokens_stripwhite(char *src, char *delimit, int *count);
+STB_EXTERN char **stb_tokens_withdelim(char *src, char *delimit, int *count);
+STB_EXTERN char **stb_tokens_quoted(char *src, char *delimit, int *count);
+// with 'quoted', allow delimiters to appear inside quotation marks, and don't
+// strip whitespace inside them (and we delete the quotation marks unless they
+// appear back to back, in which case they're considered escaped)
+
+#ifdef STB_DEFINE
+
+size_t stb_strscpy(char *d, const char *s, size_t n)
+{
+ size_t len = strlen(s);
+ if (len >= n) {
+ if (n) d[0] = 0;
+ return 0;
+ }
+ stb_p_strcpy_s(d,n+1,s);
+ return len + 1;
+}
+
+const char *stb_plural(int n)
+{
+ return n == 1 ? "" : "s";
+}
+
+int stb_prefix(char *s, char *t)
+{
+ while (*t)
+ if (*s++ != *t++)
+ return STB_FALSE;
+ return STB_TRUE;
+}
+
+int stb_prefix_count(char *s, char *t)
+{
+ int c=0;
+ while (*t) {
+ if (*s++ != *t++)
+ break;
+ ++c;
+ }
+ return c;
+}
+
+int stb_suffix(char *s, char *t)
+{
+ size_t n = strlen(s);
+ size_t m = strlen(t);
+ if (m <= n)
+ return 0 == strcmp(s+n-m, t);
+ else
+ return 0;
+}
+
+int stb_suffixi(char *s, char *t)
+{
+ size_t n = strlen(s);
+ size_t m = strlen(t);
+ if (m <= n)
+ return 0 == stb_stricmp(s+n-m, t);
+ else
+ return 0;
+}
+
+// originally I was using this table so that I could create known sentinel
+// values--e.g. change whitetable[0] to be true if I was scanning for whitespace,
+// and false if I was scanning for nonwhite. I don't appear to be using that
+// functionality anymore (I do for tokentable, though), so just replace it
+// with isspace()
+char *stb_skipwhite(char *s)
+{
+ while (isspace((unsigned char) *s)) ++s;
+ return s;
+}
+
+char *stb_skipnewline(char *s)
+{
+ if (s[0] == '\r' || s[0] == '\n') {
+ if (s[0]+s[1] == '\r' + '\n') ++s;
+ ++s;
+ }
+ return s;
+}
+
+char *stb_trimwhite(char *s)
+{
+ int i,n;
+ s = stb_skipwhite(s);
+ n = (int) strlen(s);
+ for (i=n-1; i >= 0; --i)
+ if (!isspace(s[i]))
+ break;
+ s[i+1] = 0;
+ return s;
+}
+
+char *stb_strncpy(char *s, char *t, int n)
+{
+ stb_p_strncpy_s(s,n+1,t,n);
+ s[n-1] = 0;
+ return s;
+}
+
+char *stb_substr(char *t, int n)
+{
+ char *a;
+ int z = (int) strlen(t);
+ if (z < n) n = z;
+ a = (char *) malloc(n+1);
+ stb_p_strncpy_s(a,n+1,t,n);
+ a[n] = 0;
+ return a;
+}
+
+char *stb_duplower(char *s)
+{
+ char *p = stb_p_strdup(s), *q = p;
+ while (*q) {
+ *q = tolower(*q);
+ ++q;
+ }
+ return p;
+}
+
+void stb_tolower(char *s)
+{
+ while (*s) {
+ *s = tolower(*s);
+ ++s;
+ }
+}
+
+char *stb_strchr2(char *s, char x, char y)
+{
+ for(; *s; ++s)
+ if (*s == x || *s == y)
+ return s;
+ return NULL;
+}
+
+char *stb_strrchr2(char *s, char x, char y)
+{
+ char *r = NULL;
+ for(; *s; ++s)
+ if (*s == x || *s == y)
+ r = s;
+ return r;
+}
+
+char *stb_strichr(char *s, char t)
+{
+ if (tolower(t) == toupper(t))
+ return strchr(s,t);
+ return stb_strchr2(s, (char) tolower(t), (char) toupper(t));
+}
+
+char *stb_stristr(char *s, char *t)
+{
+ size_t n = strlen(t);
+ char *z;
+ if (n==0) return s;
+ while ((z = stb_strichr(s, *t)) != NULL) {
+ if (0==stb_strnicmp(z, t, n))
+ return z;
+ s = z+1;
+ }
+ return NULL;
+}
+
+static char *stb_strtok_raw(char *output, char *src, char *delimit, int keep, int invert)
+{
+ if (invert) {
+ while (*src && strchr(delimit, *src) != NULL) {
+ *output++ = *src++;
+ }
+ } else {
+ while (*src && strchr(delimit, *src) == NULL) {
+ *output++ = *src++;
+ }
+ }
+ *output = 0;
+ if (keep)
+ return src;
+ else
+ return *src ? src+1 : src;
+}
+
+char *stb_strtok(char *output, char *src, char *delimit)
+{
+ return stb_strtok_raw(output, src, delimit, 0, 0);
+}
+
+char *stb_strtok_keep(char *output, char *src, char *delimit)
+{
+ return stb_strtok_raw(output, src, delimit, 1, 0);
+}
+
+char *stb_strtok_invert(char *output, char *src, char *delimit)
+{
+ return stb_strtok_raw(output, src, delimit, 1,1);
+}
+
+static char **stb_tokens_raw(char *src_, char *delimit, int *count,
+ int stripwhite, int allow_empty, char *start, char *end)
+{
+ int nested = 0;
+ unsigned char *src = (unsigned char *) src_;
+ static char stb_tokentable[256]; // rely on static initializion to 0
+ static char stable[256],etable[256];
+ char *out;
+ char **result;
+ int num=0;
+ unsigned char *s;
+
+ s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 1;
+ if (start) {
+ s = (unsigned char *) start; while (*s) stable[*s++] = 1;
+ s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1;
+ s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1;
+ }
+ stable[0] = 1;
+
+ // two passes through: the first time, counting how many
+ s = (unsigned char *) src;
+ while (*s) {
+ // state: just found delimiter
+ // skip further delimiters
+ if (!allow_empty) {
+ stb_tokentable[0] = 0;
+ while (stb_tokentable[*s])
+ ++s;
+ if (!*s) break;
+ }
+ ++num;
+ // skip further non-delimiters
+ stb_tokentable[0] = 1;
+ if (stripwhite == 2) { // quoted strings
+ while (!stb_tokentable[*s]) {
+ if (*s != '"')
+ ++s;
+ else {
+ ++s;
+ if (*s == '"')
+ ++s; // "" -> ", not start a string
+ else {
+ // begin a string
+ while (*s) {
+ if (s[0] == '"') {
+ if (s[1] == '"') s += 2; // "" -> "
+ else { ++s; break; } // terminating "
+ } else
+ ++s;
+ }
+ }
+ }
+ }
+ } else
+ while (nested || !stb_tokentable[*s]) {
+ if (stable[*s]) {
+ if (!*s) break;
+ if (end ? etable[*s] : nested)
+ --nested;
+ else
+ ++nested;
+ }
+ ++s;
+ }
+ if (allow_empty) {
+ if (*s) ++s;
+ }
+ }
+ // now num has the actual count... malloc our output structure
+ // need space for all the strings: strings won't be any longer than
+ // original input, since for every '\0' there's at least one delimiter
+ result = (char **) malloc(sizeof(*result) * (num+1) + (s-src+1));
+ if (result == NULL) return result;
+ out = (char *) (result + (num+1));
+ // second pass: copy out the data
+ s = (unsigned char *) src;
+ num = 0;
+ nested = 0;
+ while (*s) {
+ char *last_nonwhite;
+ // state: just found delimiter
+ // skip further delimiters
+ if (!allow_empty) {
+ stb_tokentable[0] = 0;
+ if (stripwhite)
+ while (stb_tokentable[*s] || isspace(*s))
+ ++s;
+ else
+ while (stb_tokentable[*s])
+ ++s;
+ } else if (stripwhite) {
+ while (isspace(*s)) ++s;
+ }
+ if (!*s) break;
+ // we're past any leading delimiters and whitespace
+ result[num] = out;
+ ++num;
+ // copy non-delimiters
+ stb_tokentable[0] = 1;
+ last_nonwhite = out-1;
+ if (stripwhite == 2) {
+ while (!stb_tokentable[*s]) {
+ if (*s != '"') {
+ if (!isspace(*s)) last_nonwhite = out;
+ *out++ = *s++;
+ } else {
+ ++s;
+ if (*s == '"') {
+ if (!isspace(*s)) last_nonwhite = out;
+ *out++ = *s++; // "" -> ", not start string
+ } else {
+ // begin a quoted string
+ while (*s) {
+ if (s[0] == '"') {
+ if (s[1] == '"') { *out++ = *s; s += 2; }
+ else { ++s; break; } // terminating "
+ } else
+ *out++ = *s++;
+ }
+ last_nonwhite = out-1; // all in quotes counts as non-white
+ }
+ }
+ }
+ } else {
+ while (nested || !stb_tokentable[*s]) {
+ if (!isspace(*s)) last_nonwhite = out;
+ if (stable[*s]) {
+ if (!*s) break;
+ if (end ? etable[*s] : nested)
+ --nested;
+ else
+ ++nested;
+ }
+ *out++ = *s++;
+ }
+ }
+
+ if (stripwhite) // rewind to last non-whitespace char
+ out = last_nonwhite+1;
+ *out++ = '\0';
+
+ if (*s) ++s; // skip delimiter
+ }
+ s = (unsigned char *) delimit; while (*s) stb_tokentable[*s++] = 0;
+ if (start) {
+ s = (unsigned char *) start; while (*s) stable[*s++] = 1;
+ s = (unsigned char *) end; if (s) while (*s) stable[*s++] = 1;
+ s = (unsigned char *) end; if (s) while (*s) etable[*s++] = 1;
+ }
+ if (count != NULL) *count = num;
+ result[num] = 0;
+ return result;
+}
+
+char **stb_tokens(char *src, char *delimit, int *count)
+{
+ return stb_tokens_raw(src,delimit,count,0,0,0,0);
+}
+
+char **stb_tokens_nested(char *src, char *delimit, int *count, char *nest_in, char *nest_out)
+{
+ return stb_tokens_raw(src,delimit,count,0,0,nest_in,nest_out);
+}
+
+char **stb_tokens_nested_empty(char *src, char *delimit, int *count, char *nest_in, char *nest_out)
+{
+ return stb_tokens_raw(src,delimit,count,0,1,nest_in,nest_out);
+}
+
+char **stb_tokens_allowempty(char *src, char *delimit, int *count)
+{
+ return stb_tokens_raw(src,delimit,count,0,1,0,0);
+}
+
+char **stb_tokens_stripwhite(char *src, char *delimit, int *count)
+{
+ return stb_tokens_raw(src,delimit,count,1,1,0,0);
+}
+
+char **stb_tokens_quoted(char *src, char *delimit, int *count)
+{
+ return stb_tokens_raw(src,delimit,count,2,1,0,0);
+}
+
+char *stb_dupreplace(char *src, char *find, char *replace)
+{
+ size_t len_find = strlen(find);
+ size_t len_replace = strlen(replace);
+ int count = 0;
+
+ char *s,*p,*q;
+
+ s = strstr(src, find);
+ if (s == NULL) return stb_p_strdup(src);
+ do {
+ ++count;
+ s = strstr(s + len_find, find);
+ } while (s != NULL);
+
+ p = (char *) malloc(strlen(src) + count * (len_replace - len_find) + 1);
+ if (p == NULL) return p;
+ q = p;
+ s = src;
+ for (;;) {
+ char *t = strstr(s, find);
+ if (t == NULL) {
+ stb_p_strcpy_s(q,strlen(src)+count*(len_replace-len_find)+1,s);
+ assert(strlen(p) == strlen(src) + count*(len_replace-len_find));
+ return p;
+ }
+ memcpy(q, s, t-s);
+ q += t-s;
+ memcpy(q, replace, len_replace);
+ q += len_replace;
+ s = t + len_find;
+ }
+}
+
+void stb_replaceinplace(char *src, char *find, char *replace)
+{
+ size_t len_find = strlen(find);
+ size_t len_replace = strlen(replace);
+ int delta;
+
+ char *s,*p,*q;
+
+ delta = (int) (len_replace - len_find);
+ assert(delta <= 0);
+ if (delta > 0) return;
+
+ p = strstr(src, find);
+ if (p == NULL) return;
+
+ s = q = p;
+ while (*s) {
+ memcpy(q, replace, len_replace);
+ p += len_find;
+ q += len_replace;
+ s = strstr(p, find);
+ if (s == NULL) s = p + strlen(p);
+ memmove(q, p, s-p);
+ q += s-p;
+ p = s;
+ }
+ *q = 0;
+}
+
+void stb_fixpath(char *path)
+{
+ for(; *path; ++path)
+ if (*path == '\\')
+ *path = '/';
+}
+
+void stb__add_section(char *buffer, char *data, ptrdiff_t curlen, ptrdiff_t newlen)
+{
+ if (newlen < curlen) {
+ ptrdiff_t z1 = newlen >> 1, z2 = newlen-z1;
+ memcpy(buffer, data, z1-1);
+ buffer[z1-1] = '.';
+ buffer[z1-0] = '.';
+ memcpy(buffer+z1+1, data+curlen-z2+1, z2-1);
+ } else
+ memcpy(buffer, data, curlen);
+}
+
+char * stb_shorten_path_readable(char *path, int len)
+{
+ static char buffer[1024];
+ ptrdiff_t n = strlen(path),n1,n2,r1,r2;
+ char *s;
+ if (n <= len) return path;
+ if (len > 1024) return path;
+ s = stb_strrchr2(path, '/', '\\');
+ if (s) {
+ n1 = s - path + 1;
+ n2 = n - n1;
+ ++s;
+ } else {
+ n1 = 0;
+ n2 = n;
+ s = path;
+ }
+ // now we need to reduce r1 and r2 so that they fit in len
+ if (n1 < len>>1) {
+ r1 = n1;
+ r2 = len - r1;
+ } else if (n2 < len >> 1) {
+ r2 = n2;
+ r1 = len - r2;
+ } else {
+ r1 = n1 * len / n;
+ r2 = n2 * len / n;
+ if (r1 < len>>2) r1 = len>>2, r2 = len-r1;
+ if (r2 < len>>2) r2 = len>>2, r1 = len-r2;
+ }
+ assert(r1 <= n1 && r2 <= n2);
+ if (n1)
+ stb__add_section(buffer, path, n1, r1);
+ stb__add_section(buffer+r1, s, n2, r2);
+ buffer[len] = 0;
+ return buffer;
+}
+
+static char *stb__splitpath_raw(char *buffer, char *path, int flag)
+{
+ ptrdiff_t len=0,x,y, n = (int) strlen(path), f1,f2;
+ char *s = stb_strrchr2(path, '/', '\\');
+ char *t = strrchr(path, '.');
+ if (s && t && t < s) t = NULL;
+ if (s) ++s;
+
+ if (flag == STB_EXT_NO_PERIOD)
+ flag |= STB_EXT;
+
+ if (!(flag & (STB_PATH | STB_FILE | STB_EXT))) return NULL;
+
+ f1 = s == NULL ? 0 : s-path; // start of filename
+ f2 = t == NULL ? n : t-path; // just past end of filename
+
+ if (flag & STB_PATH) {
+ x = 0; if (f1 == 0 && flag == STB_PATH) len=2;
+ } else if (flag & STB_FILE) {
+ x = f1;
+ } else {
+ x = f2;
+ if (flag & STB_EXT_NO_PERIOD)
+ if (buffer[x] == '.')
+ ++x;
+ }
+
+ if (flag & STB_EXT)
+ y = n;
+ else if (flag & STB_FILE)
+ y = f2;
+ else
+ y = f1;
+
+ if (buffer == NULL) {
+ buffer = (char *) malloc(y-x + len + 1);
+ if (!buffer) return NULL;
+ }
+
+ if (len) { stb_p_strcpy_s(buffer, sizeof(buffer), "./"); return buffer; }
+ stb_p_strncpy_s(buffer, sizeof(buffer),path+x, y-x);
+ buffer[y-x] = 0;
+ return buffer;
+}
+
+char *stb_splitpath(char *output, char *src, int flag)
+{
+ return stb__splitpath_raw(output, src, flag);
+}
+
+char *stb_splitpathdup(char *src, int flag)
+{
+ return stb__splitpath_raw(NULL, src, flag);
+}
+
+char *stb_replacedir(char *output, char *src, char *dir)
+{
+ char buffer[4096];
+ stb_splitpath(buffer, src, STB_FILE | STB_EXT);
+ if (dir)
+ stb_p_sprintf(output stb_p_size(9999), "%s/%s", dir, buffer);
+ else
+ stb_p_strcpy_s(output, sizeof(buffer), buffer); // @UNSAFE
+ return output;
+}
+
+char *stb_replaceext(char *output, char *src, char *ext)
+{
+ char buffer[4096];
+ stb_splitpath(buffer, src, STB_PATH | STB_FILE);
+ if (ext)
+ stb_p_sprintf(output stb_p_size(9999), "%s.%s", buffer, ext[0] == '.' ? ext+1 : ext);
+ else
+ stb_p_strcpy_s(output, sizeof(buffer), buffer); // @UNSAFE
+ return output;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_alloc - hierarchical allocator
+//
+// inspired by http://swapped.cc/halloc
+//
+//
+// When you alloc a given block through stb_alloc, you have these choices:
+//
+// 1. does it have a parent?
+// 2. can it have children?
+// 3. can it be freed directly?
+// 4. is it transferrable?
+// 5. what is its alignment?
+//
+// Here are interesting combinations of those:
+//
+// children free transfer alignment
+// arena Y Y N n/a
+// no-overhead, chunked N N N normal
+// string pool alloc N N N 1
+// parent-ptr, chunked Y N N normal
+// low-overhead, unchunked N Y Y normal
+// general purpose alloc Y Y Y normal
+//
+// Unchunked allocations will probably return 16-aligned pointers. If
+// we 16-align the results, we have room for 4 pointers. For smaller
+// allocations that allow finer alignment, we can reduce the pointers.
+//
+// The strategy is that given a pointer, assuming it has a header (only
+// the no-overhead allocations have no header), we can determine the
+// type of the header fields, and the number of them, by stepping backwards
+// through memory and looking at the tags in the bottom bits.
+//
+// Implementation strategy:
+// chunked allocations come from the middle of chunks, and can't
+// be freed. thefore they do not need to be on a sibling chain.
+// they may need child pointers if they have children.
+//
+// chunked, with-children
+// void *parent;
+//
+// unchunked, no-children -- reduced storage
+// void *next_sibling;
+// void *prev_sibling_nextp;
+//
+// unchunked, general
+// void *first_child;
+// void *next_sibling;
+// void *prev_sibling_nextp;
+// void *chunks;
+//
+// so, if we code each of these fields with different bit patterns
+// (actually same one for next/prev/child), then we can identify which
+// each one is from the last field.
+
+STB_EXTERN void stb_free(void *p);
+STB_EXTERN void *stb_malloc_global(size_t size);
+STB_EXTERN void *stb_malloc(void *context, size_t size);
+STB_EXTERN void *stb_malloc_nofree(void *context, size_t size);
+STB_EXTERN void *stb_malloc_leaf(void *context, size_t size);
+STB_EXTERN void *stb_malloc_raw(void *context, size_t size);
+STB_EXTERN void *stb_realloc(void *ptr, size_t newsize);
+
+STB_EXTERN void stb_reassign(void *new_context, void *ptr);
+STB_EXTERN void stb_malloc_validate(void *p, void *parent);
+
+extern int stb_alloc_chunk_size ;
+extern int stb_alloc_count_free ;
+extern int stb_alloc_count_alloc;
+extern int stb_alloc_alignment ;
+
+#ifdef STB_DEFINE
+
+int stb_alloc_chunk_size = 65536;
+int stb_alloc_count_free = 0;
+int stb_alloc_count_alloc = 0;
+int stb_alloc_alignment = -16;
+
+typedef struct stb__chunk
+{
+ struct stb__chunk *next;
+ int data_left;
+ int alloc;
+} stb__chunk;
+
+typedef struct
+{
+ void * next;
+ void ** prevn;
+} stb__nochildren;
+
+typedef struct
+{
+ void ** prevn;
+ void * child;
+ void * next;
+ stb__chunk *chunks;
+} stb__alloc;
+
+typedef struct
+{
+ stb__alloc *parent;
+} stb__chunked;
+
+#define STB__PARENT 1
+#define STB__CHUNKS 2
+
+typedef enum
+{
+ STB__nochildren = 0,
+ STB__chunked = STB__PARENT,
+ STB__alloc = STB__CHUNKS,
+
+ STB__chunk_raw = 4,
+} stb__alloc_type;
+
+// these functions set the bottom bits of a pointer efficiently
+#define STB__DECODE(x,v) ((void *) ((char *) (x) - (v)))
+#define STB__ENCODE(x,v) ((void *) ((char *) (x) + (v)))
+
+#define stb__parent(z) (stb__alloc *) STB__DECODE((z)->parent, STB__PARENT)
+#define stb__chunks(z) (stb__chunk *) STB__DECODE((z)->chunks, STB__CHUNKS)
+
+#define stb__setparent(z,p) (z)->parent = (stb__alloc *) STB__ENCODE((p), STB__PARENT)
+#define stb__setchunks(z,c) (z)->chunks = (stb__chunk *) STB__ENCODE((c), STB__CHUNKS)
+
+static stb__alloc stb__alloc_global =
+{
+ NULL,
+ NULL,
+ NULL,
+ (stb__chunk *) STB__ENCODE(NULL, STB__CHUNKS)
+};
+
+static stb__alloc_type stb__identify(void *p)
+{
+ void **q = (void **) p;
+ return (stb__alloc_type) ((stb_uinta) q[-1] & 3);
+}
+
+static void *** stb__prevn(void *p)
+{
+ if (stb__identify(p) == STB__alloc) {
+ stb__alloc *s = (stb__alloc *) p - 1;
+ return &s->prevn;
+ } else {
+ stb__nochildren *s = (stb__nochildren *) p - 1;
+ return &s->prevn;
+ }
+}
+
+void stb_free(void *p)
+{
+ if (p == NULL) return;
+
+ // count frees so that unit tests can see what's happening
+ ++stb_alloc_count_free;
+
+ switch(stb__identify(p)) {
+ case STB__chunked:
+ // freeing a chunked-block with children does nothing;
+ // they only get freed when the parent does
+ // surely this is wrong, and it should free them immediately?
+ // otherwise how are they getting put on the right chain?
+ return;
+ case STB__nochildren: {
+ stb__nochildren *s = (stb__nochildren *) p - 1;
+ // unlink from sibling chain
+ *(s->prevn) = s->next;
+ if (s->next)
+ *stb__prevn(s->next) = s->prevn;
+ free(s);
+ return;
+ }
+ case STB__alloc: {
+ stb__alloc *s = (stb__alloc *) p - 1;
+ stb__chunk *c, *n;
+ void *q;
+
+ // unlink from sibling chain, if any
+ *(s->prevn) = s->next;
+ if (s->next)
+ *stb__prevn(s->next) = s->prevn;
+
+ // first free chunks
+ c = (stb__chunk *) stb__chunks(s);
+ while (c != NULL) {
+ n = c->next;
+ stb_alloc_count_free += c->alloc;
+ free(c);
+ c = n;
+ }
+
+ // validating
+ stb__setchunks(s,NULL);
+ s->prevn = NULL;
+ s->next = NULL;
+
+ // now free children
+ while ((q = s->child) != NULL) {
+ stb_free(q);
+ }
+
+ // now free self
+ free(s);
+ return;
+ }
+ default:
+ assert(0); /* NOTREACHED */
+ }
+}
+
+void stb_malloc_validate(void *p, void *parent)
+{
+ if (p == NULL) return;
+
+ switch(stb__identify(p)) {
+ case STB__chunked:
+ return;
+ case STB__nochildren: {
+ stb__nochildren *n = (stb__nochildren *) p - 1;
+ if (n->prevn)
+ assert(*n->prevn == p);
+ if (n->next) {
+ assert(*stb__prevn(n->next) == &n->next);
+ stb_malloc_validate(n, parent);
+ }
+ return;
+ }
+ case STB__alloc: {
+ stb__alloc *s = (stb__alloc *) p - 1;
+
+ if (s->prevn)
+ assert(*s->prevn == p);
+
+ if (s->child) {
+ assert(*stb__prevn(s->child) == &s->child);
+ stb_malloc_validate(s->child, p);
+ }
+
+ if (s->next) {
+ assert(*stb__prevn(s->next) == &s->next);
+ stb_malloc_validate(s->next, parent);
+ }
+ return;
+ }
+ default:
+ assert(0); /* NOTREACHED */
+ }
+}
+
+static void * stb__try_chunk(stb__chunk *c, int size, int align, int pre_align)
+{
+ char *memblock = (char *) (c+1), *q;
+ stb_inta iq;
+ int start_offset;
+
+ // we going to allocate at the end of the chunk, not the start. confusing,
+ // but it means we don't need both a 'limit' and a 'cur', just a 'cur'.
+ // the block ends at: p + c->data_left
+ // then we move back by size
+ start_offset = c->data_left - size;
+
+ // now we need to check the alignment of that
+ q = memblock + start_offset;
+ iq = (stb_inta) q;
+ assert(sizeof(q) == sizeof(iq));
+
+ // suppose align = 2
+ // then we need to retreat iq far enough that (iq & (2-1)) == 0
+ // to get (iq & (align-1)) = 0 requires subtracting (iq & (align-1))
+
+ start_offset -= iq & (align-1);
+ assert(((stb_uinta) (memblock+start_offset) & (align-1)) == 0);
+
+ // now, if that + pre_align works, go for it!
+ start_offset -= pre_align;
+
+ if (start_offset >= 0) {
+ c->data_left = start_offset;
+ return memblock + start_offset;
+ }
+
+ return NULL;
+}
+
+static void stb__sort_chunks(stb__alloc *src)
+{
+ // of the first two chunks, put the chunk with more data left in it first
+ stb__chunk *c = stb__chunks(src), *d;
+ if (c == NULL) return;
+ d = c->next;
+ if (d == NULL) return;
+ if (c->data_left > d->data_left) return;
+
+ c->next = d->next;
+ d->next = c;
+ stb__setchunks(src, d);
+}
+
+static void * stb__alloc_chunk(stb__alloc *src, int size, int align, int pre_align)
+{
+ void *p;
+ stb__chunk *c = stb__chunks(src);
+
+ if (c && size <= stb_alloc_chunk_size) {
+
+ p = stb__try_chunk(c, size, align, pre_align);
+ if (p) { ++c->alloc; return p; }
+
+ // try a second chunk to reduce wastage
+ if (c->next) {
+ p = stb__try_chunk(c->next, size, align, pre_align);
+ if (p) { ++c->alloc; return p; }
+
+ // put the bigger chunk first, since the second will get buried
+ // the upshot of this is that, until it gets allocated from, chunk #2
+ // is always the largest remaining chunk. (could formalize
+ // this with a heap!)
+ stb__sort_chunks(src);
+ c = stb__chunks(src);
+ }
+ }
+
+ // allocate a new chunk
+ {
+ stb__chunk *n;
+
+ int chunk_size = stb_alloc_chunk_size;
+ // we're going to allocate a new chunk to put this in
+ if (size > chunk_size)
+ chunk_size = size;
+
+ assert(sizeof(*n) + pre_align <= 16);
+
+ // loop trying to allocate a large enough chunk
+ // the loop is because the alignment may cause problems if it's big...
+ // and we don't know what our chunk alignment is going to be
+ while (1) {
+ n = (stb__chunk *) malloc(16 + chunk_size);
+ if (n == NULL) return NULL;
+
+ n->data_left = chunk_size - sizeof(*n);
+
+ p = stb__try_chunk(n, size, align, pre_align);
+ if (p != NULL) {
+ n->next = c;
+ stb__setchunks(src, n);
+
+ // if we just used up the whole block immediately,
+ // move the following chunk up
+ n->alloc = 1;
+ if (size == chunk_size)
+ stb__sort_chunks(src);
+
+ return p;
+ }
+
+ free(n);
+ chunk_size += 16+align;
+ }
+ }
+}
+
+static stb__alloc * stb__get_context(void *context)
+{
+ if (context == NULL) {
+ return &stb__alloc_global;
+ } else {
+ int u = stb__identify(context);
+ // if context is chunked, grab parent
+ if (u == STB__chunked) {
+ stb__chunked *s = (stb__chunked *) context - 1;
+ return stb__parent(s);
+ } else {
+ return (stb__alloc *) context - 1;
+ }
+ }
+}
+
+static void stb__insert_alloc(stb__alloc *src, stb__alloc *s)
+{
+ s->prevn = &src->child;
+ s->next = src->child;
+ src->child = s+1;
+ if (s->next)
+ *stb__prevn(s->next) = &s->next;
+}
+
+static void stb__insert_nochild(stb__alloc *src, stb__nochildren *s)
+{
+ s->prevn = &src->child;
+ s->next = src->child;
+ src->child = s+1;
+ if (s->next)
+ *stb__prevn(s->next) = &s->next;
+}
+
+static void * malloc_base(void *context, size_t size, stb__alloc_type t, int align)
+{
+ void *p;
+
+ stb__alloc *src = stb__get_context(context);
+
+ if (align <= 0) {
+ // compute worst-case C packed alignment
+ // e.g. a 24-byte struct is 8-aligned
+ int align_proposed = 1 << stb_lowbit8((unsigned int) size);
+
+ if (align_proposed < 0)
+ align_proposed = 4;
+
+ if (align_proposed == 0) {
+ if (size == 0)
+ align_proposed = 1;
+ else
+ align_proposed = 256;
+ }
+
+ // a negative alignment means 'don't align any larger
+ // than this'; so -16 means we align 1,2,4,8, or 16
+
+ if (align < 0) {
+ if (align_proposed > -align)
+ align_proposed = -align;
+ }
+
+ align = align_proposed;
+ }
+
+ assert(stb_is_pow2(align));
+
+ // don't cause misalignment when allocating nochildren
+ if (t == STB__nochildren && align > 8)
+ t = STB__alloc;
+
+ switch (t) {
+ case STB__alloc: {
+ stb__alloc *s = (stb__alloc *) malloc(size + sizeof(*s));
+ if (s == NULL) return NULL;
+ p = s+1;
+ s->child = NULL;
+ stb__insert_alloc(src, s);
+
+ stb__setchunks(s,NULL);
+ break;
+ }
+
+ case STB__nochildren: {
+ stb__nochildren *s = (stb__nochildren *) malloc(size + sizeof(*s));
+ if (s == NULL) return NULL;
+ p = s+1;
+ stb__insert_nochild(src, s);
+ break;
+ }
+
+ case STB__chunk_raw: {
+ p = stb__alloc_chunk(src, (int) size, align, 0);
+ if (p == NULL) return NULL;
+ break;
+ }
+
+ case STB__chunked: {
+ stb__chunked *s;
+ if (align < sizeof(stb_uintptr)) align = sizeof(stb_uintptr);
+ s = (stb__chunked *) stb__alloc_chunk(src, (int) size, align, sizeof(*s));
+ if (s == NULL) return NULL;
+ stb__setparent(s, src);
+ p = s+1;
+ break;
+ }
+
+ default: p = NULL; assert(0); /* NOTREACHED */
+ }
+
+ ++stb_alloc_count_alloc;
+ return p;
+}
+
+void *stb_malloc_global(size_t size)
+{
+ return malloc_base(NULL, size, STB__alloc, stb_alloc_alignment);
+}
+
+void *stb_malloc(void *context, size_t size)
+{
+ return malloc_base(context, size, STB__alloc, stb_alloc_alignment);
+}
+
+void *stb_malloc_nofree(void *context, size_t size)
+{
+ return malloc_base(context, size, STB__chunked, stb_alloc_alignment);
+}
+
+void *stb_malloc_leaf(void *context, size_t size)
+{
+ return malloc_base(context, size, STB__nochildren, stb_alloc_alignment);
+}
+
+void *stb_malloc_raw(void *context, size_t size)
+{
+ return malloc_base(context, size, STB__chunk_raw, stb_alloc_alignment);
+}
+
+char *stb_malloc_string(void *context, size_t size)
+{
+ return (char *) malloc_base(context, size, STB__chunk_raw, 1);
+}
+
+void *stb_realloc(void *ptr, size_t newsize)
+{
+ stb__alloc_type t;
+
+ if (ptr == NULL) return stb_malloc(NULL, newsize);
+ if (newsize == 0) { stb_free(ptr); return NULL; }
+
+ t = stb__identify(ptr);
+ assert(t == STB__alloc || t == STB__nochildren);
+
+ if (t == STB__alloc) {
+ stb__alloc *s = (stb__alloc *) ptr - 1;
+
+ s = (stb__alloc *) realloc(s, newsize + sizeof(*s));
+ if (s == NULL) return NULL;
+
+ ptr = s+1;
+
+ // update pointers
+ (*s->prevn) = ptr;
+ if (s->next)
+ *stb__prevn(s->next) = &s->next;
+
+ if (s->child)
+ *stb__prevn(s->child) = &s->child;
+
+ return ptr;
+ } else {
+ stb__nochildren *s = (stb__nochildren *) ptr - 1;
+
+ s = (stb__nochildren *) realloc(ptr, newsize + sizeof(s));
+ if (s == NULL) return NULL;
+
+ // update pointers
+ (*s->prevn) = s+1;
+ if (s->next)
+ *stb__prevn(s->next) = &s->next;
+
+ return s+1;
+ }
+}
+
+void *stb_realloc_c(void *context, void *ptr, size_t newsize)
+{
+ if (ptr == NULL) return stb_malloc(context, newsize);
+ if (newsize == 0) { stb_free(ptr); return NULL; }
+ // @TODO: verify you haven't changed contexts
+ return stb_realloc(ptr, newsize);
+}
+
+void stb_reassign(void *new_context, void *ptr)
+{
+ stb__alloc *src = stb__get_context(new_context);
+
+ stb__alloc_type t = stb__identify(ptr);
+ assert(t == STB__alloc || t == STB__nochildren);
+
+ if (t == STB__alloc) {
+ stb__alloc *s = (stb__alloc *) ptr - 1;
+
+ // unlink from old
+ *(s->prevn) = s->next;
+ if (s->next)
+ *stb__prevn(s->next) = s->prevn;
+
+ stb__insert_alloc(src, s);
+ } else {
+ stb__nochildren *s = (stb__nochildren *) ptr - 1;
+
+ // unlink from old
+ *(s->prevn) = s->next;
+ if (s->next)
+ *stb__prevn(s->next) = s->prevn;
+
+ stb__insert_nochild(src, s);
+ }
+}
+
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_arr
+//
+// An stb_arr is directly useable as a pointer (use the actual type in your
+// definition), but when it resizes, it returns a new pointer and you can't
+// use the old one, so you have to be careful to copy-in-out as necessary.
+//
+// Use a NULL pointer as a 0-length array.
+//
+// float *my_array = NULL, *temp;
+//
+// // add elements on the end one at a time
+// stb_arr_push(my_array, 0.0f);
+// stb_arr_push(my_array, 1.0f);
+// stb_arr_push(my_array, 2.0f);
+//
+// assert(my_array[1] == 2.0f);
+//
+// // add an uninitialized element at the end, then assign it
+// *stb_arr_add(my_array) = 3.0f;
+//
+// // add three uninitialized elements at the end
+// temp = stb_arr_addn(my_array,3);
+// temp[0] = 4.0f;
+// temp[1] = 5.0f;
+// temp[2] = 6.0f;
+//
+// assert(my_array[5] == 5.0f);
+//
+// // remove the last one
+// stb_arr_pop(my_array);
+//
+// assert(stb_arr_len(my_array) == 6);
+
+
+#ifdef STB_MALLOC_WRAPPER
+ #define STB__PARAMS , char *file, int line
+ #define STB__ARGS , file, line
+#else
+ #define STB__PARAMS
+ #define STB__ARGS
+#endif
+
+// calling this function allocates an empty stb_arr attached to p
+// (whereas NULL isn't attached to anything)
+STB_EXTERN void stb_arr_malloc(void **target, void *context);
+
+// call this function with a non-NULL value to have all successive
+// stbs that are created be attached to the associated parent. Note
+// that once a given stb_arr is non-empty, it stays attached to its
+// current parent, even if you call this function again.
+// it turns the previous value, so you can restore it
+STB_EXTERN void* stb_arr_malloc_parent(void *p);
+
+// simple functions written on top of other functions
+#define stb_arr_empty(a) ( stb_arr_len(a) == 0 )
+#define stb_arr_add(a) ( stb_arr_addn((a),1) )
+#define stb_arr_push(a,v) ( *stb_arr_add(a)=(v) )
+
+typedef struct
+{
+ int len, limit;
+ int stb_malloc;
+ unsigned int signature;
+} stb__arr;
+
+#define stb_arr_signature 0x51bada7b // ends with 0123 in decimal
+
+// access the header block stored before the data
+#define stb_arrhead(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1)
+#define stb_arrhead2(a) /*lint --e(826)*/ (((stb__arr *) (a)) - 1)
+
+#ifdef STB_DEBUG
+#define stb_arr_check(a) assert(!a || stb_arrhead(a)->signature == stb_arr_signature)
+#define stb_arr_check2(a) assert(!a || stb_arrhead2(a)->signature == stb_arr_signature)
+#else
+#define stb_arr_check(a) ((void) 0)
+#define stb_arr_check2(a) ((void) 0)
+#endif
+
+// ARRAY LENGTH
+
+// get the array length; special case if pointer is NULL
+#define stb_arr_len(a) (a ? stb_arrhead(a)->len : 0)
+#define stb_arr_len2(a) ((stb__arr *) (a) ? stb_arrhead2(a)->len : 0)
+#define stb_arr_lastn(a) (stb_arr_len(a)-1)
+
+// check whether a given index is valid -- tests 0 <= i < stb_arr_len(a)
+#define stb_arr_valid(a,i) (a ? (int) (i) < stb_arrhead(a)->len : 0)
+
+// change the array length so is is exactly N entries long, creating
+// uninitialized entries as needed
+#define stb_arr_setlen(a,n) \
+ (stb__arr_setlen((void **) &(a), sizeof(a[0]), (n)))
+
+// change the array length so that N is a valid index (that is, so
+// it is at least N entries long), creating uninitialized entries as needed
+#define stb_arr_makevalid(a,n) \
+ (stb_arr_len(a) < (n)+1 ? stb_arr_setlen(a,(n)+1),(a) : (a))
+
+// remove the last element of the array, returning it
+#define stb_arr_pop(a) ((stb_arr_check(a), (a))[--stb_arrhead(a)->len])
+
+// access the last element in the array
+#define stb_arr_last(a) ((stb_arr_check(a), (a))[stb_arr_len(a)-1])
+
+// is iterator at end of list?
+#define stb_arr_end(a,i) ((i) >= &(a)[stb_arr_len(a)])
+
+// (internal) change the allocated length of the array
+#define stb_arr__grow(a,n) (stb_arr_check(a), stb_arrhead(a)->len += (n))
+
+// add N new uninitialized elements to the end of the array
+#define stb_arr__addn(a,n) /*lint --e(826)*/ \
+ ((stb_arr_len(a)+(n) > stb_arrcurmax(a)) \
+ ? (stb__arr_addlen((void **) &(a),sizeof(*a),(n)),0) \
+ : ((stb_arr__grow(a,n), 0)))
+
+// add N new uninitialized elements to the end of the array, and return
+// a pointer to the first new one
+#define stb_arr_addn(a,n) (stb_arr__addn((a),n),(a)+stb_arr_len(a)-(n))
+
+// add N new uninitialized elements starting at index 'i'
+#define stb_arr_insertn(a,i,n) (stb__arr_insertn((void **) &(a), sizeof(*a), i, n))
+
+// insert an element at i
+#define stb_arr_insert(a,i,v) (stb__arr_insertn((void **) &(a), sizeof(*a), i, 1), ((a)[i] = v))
+
+// delete N elements from the middle starting at index 'i'
+#define stb_arr_deleten(a,i,n) (stb__arr_deleten((void **) &(a), sizeof(*a), i, n))
+
+// delete the i'th element
+#define stb_arr_delete(a,i) stb_arr_deleten(a,i,1)
+
+// delete the i'th element, swapping down from the end
+#define stb_arr_fastdelete(a,i) \
+ (stb_swap(&a[i], &a[stb_arrhead(a)->len-1], sizeof(*a)), stb_arr_pop(a))
+
+
+// ARRAY STORAGE
+
+// get the array maximum storage; special case if NULL
+#define stb_arrcurmax(a) (a ? stb_arrhead(a)->limit : 0)
+#define stb_arrcurmax2(a) (a ? stb_arrhead2(a)->limit : 0)
+
+// set the maxlength of the array to n in anticipation of further growth
+#define stb_arr_setsize(a,n) (stb_arr_check(a), stb__arr_setsize((void **) &(a),sizeof((a)[0]),n))
+
+// make sure maxlength is large enough for at least N new allocations
+#define stb_arr_atleast(a,n) (stb_arr_len(a)+(n) > stb_arrcurmax(a) \
+ ? stb_arr_setsize((a), (n)) : 0)
+
+// make a copy of a given array (copies contents via 'memcpy'!)
+#define stb_arr_copy(a) stb__arr_copy(a, sizeof((a)[0]))
+
+// compute the storage needed to store all the elements of the array
+#define stb_arr_storage(a) (stb_arr_len(a) * sizeof((a)[0]))
+
+#define stb_arr_for(v,arr) for((v)=(arr); (v) < (arr)+stb_arr_len(arr); ++(v))
+
+// IMPLEMENTATION
+
+STB_EXTERN void stb_arr_free_(void **p);
+STB_EXTERN void *stb__arr_copy_(void *p, int elem_size);
+STB_EXTERN void stb__arr_setsize_(void **p, int size, int limit STB__PARAMS);
+STB_EXTERN void stb__arr_setlen_(void **p, int size, int newlen STB__PARAMS);
+STB_EXTERN void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS);
+STB_EXTERN void stb__arr_deleten_(void **p, int size, int loc, int n STB__PARAMS);
+STB_EXTERN void stb__arr_insertn_(void **p, int size, int loc, int n STB__PARAMS);
+
+#define stb_arr_free(p) stb_arr_free_((void **) &(p))
+#define stb__arr_copy stb__arr_copy_
+
+#ifndef STB_MALLOC_WRAPPER
+ #define stb__arr_setsize stb__arr_setsize_
+ #define stb__arr_setlen stb__arr_setlen_
+ #define stb__arr_addlen stb__arr_addlen_
+ #define stb__arr_deleten stb__arr_deleten_
+ #define stb__arr_insertn stb__arr_insertn_
+#else
+ #define stb__arr_addlen(p,s,n) stb__arr_addlen_(p,s,n,__FILE__,__LINE__)
+ #define stb__arr_setlen(p,s,n) stb__arr_setlen_(p,s,n,__FILE__,__LINE__)
+ #define stb__arr_setsize(p,s,n) stb__arr_setsize_(p,s,n,__FILE__,__LINE__)
+ #define stb__arr_deleten(p,s,i,n) stb__arr_deleten_(p,s,i,n,__FILE__,__LINE__)
+ #define stb__arr_insertn(p,s,i,n) stb__arr_insertn_(p,s,i,n,__FILE__,__LINE__)
+#endif
+
+#ifdef STB_DEFINE
+static void *stb__arr_context;
+
+void *stb_arr_malloc_parent(void *p)
+{
+ void *q = stb__arr_context;
+ stb__arr_context = p;
+ return q;
+}
+
+void stb_arr_malloc(void **target, void *context)
+{
+ stb__arr *q = (stb__arr *) stb_malloc(context, sizeof(*q));
+ q->len = q->limit = 0;
+ q->stb_malloc = 1;
+ q->signature = stb_arr_signature;
+ *target = (void *) (q+1);
+}
+
+static void * stb__arr_malloc(int size)
+{
+ if (stb__arr_context)
+ return stb_malloc(stb__arr_context, size);
+ return malloc(size);
+}
+
+void * stb__arr_copy_(void *p, int elem_size)
+{
+ stb__arr *q;
+ if (p == NULL) return p;
+ q = (stb__arr *) stb__arr_malloc(sizeof(*q) + elem_size * stb_arrhead2(p)->limit);
+ stb_arr_check2(p);
+ memcpy(q, stb_arrhead2(p), sizeof(*q) + elem_size * stb_arrhead2(p)->len);
+ q->stb_malloc = !!stb__arr_context;
+ return q+1;
+}
+
+void stb_arr_free_(void **pp)
+{
+ void *p = *pp;
+ stb_arr_check2(p);
+ if (p) {
+ stb__arr *q = stb_arrhead2(p);
+ if (q->stb_malloc)
+ stb_free(q);
+ else
+ free(q);
+ }
+ *pp = NULL;
+}
+
+static void stb__arrsize_(void **pp, int size, int limit, int len STB__PARAMS)
+{
+ void *p = *pp;
+ stb__arr *a;
+ stb_arr_check2(p);
+ if (p == NULL) {
+ if (len == 0 && size == 0) return;
+ a = (stb__arr *) stb__arr_malloc(sizeof(*a) + size*limit);
+ a->limit = limit;
+ a->len = len;
+ a->stb_malloc = !!stb__arr_context;
+ a->signature = stb_arr_signature;
+ } else {
+ a = stb_arrhead2(p);
+ a->len = len;
+ if (a->limit < limit) {
+ void *p;
+ if (a->limit >= 4 && limit < a->limit * 2)
+ limit = a->limit * 2;
+ if (a->stb_malloc)
+ p = stb_realloc(a, sizeof(*a) + limit*size);
+ else
+ #ifdef STB_MALLOC_WRAPPER
+ p = stb__realloc(a, sizeof(*a) + limit*size, file, line);
+ #else
+ p = realloc(a, sizeof(*a) + limit*size);
+ #endif
+ if (p) {
+ a = (stb__arr *) p;
+ a->limit = limit;
+ } else {
+ // throw an error!
+ }
+ }
+ }
+ a->len = stb_min(a->len, a->limit);
+ *pp = a+1;
+}
+
+void stb__arr_setsize_(void **pp, int size, int limit STB__PARAMS)
+{
+ void *p = *pp;
+ stb_arr_check2(p);
+ stb__arrsize_(pp, size, limit, stb_arr_len2(p) STB__ARGS);
+}
+
+void stb__arr_setlen_(void **pp, int size, int newlen STB__PARAMS)
+{
+ void *p = *pp;
+ stb_arr_check2(p);
+ if (stb_arrcurmax2(p) < newlen || p == NULL) {
+ stb__arrsize_(pp, size, newlen, newlen STB__ARGS);
+ } else {
+ stb_arrhead2(p)->len = newlen;
+ }
+}
+
+void stb__arr_addlen_(void **p, int size, int addlen STB__PARAMS)
+{
+ stb__arr_setlen_(p, size, stb_arr_len2(*p) + addlen STB__ARGS);
+}
+
+void stb__arr_insertn_(void **pp, int size, int i, int n STB__PARAMS)
+{
+ void *p = *pp;
+ if (n) {
+ int z;
+
+ if (p == NULL) {
+ stb__arr_addlen_(pp, size, n STB__ARGS);
+ return;
+ }
+
+ z = stb_arr_len2(p);
+ stb__arr_addlen_(&p, size, n STB__ARGS);
+ memmove((char *) p + (i+n)*size, (char *) p + i*size, size * (z-i));
+ }
+ *pp = p;
+}
+
+void stb__arr_deleten_(void **pp, int size, int i, int n STB__PARAMS)
+{
+ void *p = *pp;
+ if (n) {
+ memmove((char *) p + i*size, (char *) p + (i+n)*size, size * (stb_arr_len2(p)-(i+n)));
+ stb_arrhead2(p)->len -= n;
+ }
+ *pp = p;
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Hashing
+//
+// typical use for this is to make a power-of-two hash table.
+//
+// let N = size of table (2^n)
+// let H = stb_hash(str)
+// let S = stb_rehash(H) | 1
+//
+// then hash probe sequence P(i) for i=0..N-1
+// P(i) = (H + S*i) & (N-1)
+//
+// the idea is that H has 32 bits of hash information, but the
+// table has only, say, 2^20 entries so only uses 20 of the bits.
+// then by rehashing the original H we get 2^12 different probe
+// sequences for a given initial probe location. (So it's optimal
+// for 64K tables and its optimality decreases past that.)
+//
+// ok, so I've added something that generates _two separate_
+// 32-bit hashes simultaneously which should scale better to
+// very large tables.
+
+
+STB_EXTERN unsigned int stb_hash(char *str);
+STB_EXTERN unsigned int stb_hashptr(void *p);
+STB_EXTERN unsigned int stb_hashlen(char *str, int len);
+STB_EXTERN unsigned int stb_rehash_improved(unsigned int v);
+STB_EXTERN unsigned int stb_hash_fast(void *p, int len);
+STB_EXTERN unsigned int stb_hash2(char *str, unsigned int *hash2_ptr);
+STB_EXTERN unsigned int stb_hash_number(unsigned int hash);
+
+#define stb_rehash(x) ((x) + ((x) >> 6) + ((x) >> 19))
+
+#ifdef STB_DEFINE
+unsigned int stb_hash(char *str)
+{
+ unsigned int hash = 0;
+ while (*str)
+ hash = (hash << 7) + (hash >> 25) + *str++;
+ return hash + (hash >> 16);
+}
+
+unsigned int stb_hashlen(char *str, int len)
+{
+ unsigned int hash = 0;
+ while (len-- > 0 && *str)
+ hash = (hash << 7) + (hash >> 25) + *str++;
+ return hash + (hash >> 16);
+}
+
+unsigned int stb_hashptr(void *p)
+{
+ unsigned int x = (unsigned int)(size_t) p;
+
+ // typically lacking in low bits and high bits
+ x = stb_rehash(x);
+ x += x << 16;
+
+ // pearson's shuffle
+ x ^= x << 3;
+ x += x >> 5;
+ x ^= x << 2;
+ x += x >> 15;
+ x ^= x << 10;
+ return stb_rehash(x);
+}
+
+unsigned int stb_rehash_improved(unsigned int v)
+{
+ return stb_hashptr((void *)(size_t) v);
+}
+
+unsigned int stb_hash2(char *str, unsigned int *hash2_ptr)
+{
+ unsigned int hash1 = 0x3141592c;
+ unsigned int hash2 = 0x77f044ed;
+ while (*str) {
+ hash1 = (hash1 << 7) + (hash1 >> 25) + *str;
+ hash2 = (hash2 << 11) + (hash2 >> 21) + *str;
+ ++str;
+ }
+ *hash2_ptr = hash2 + (hash1 >> 16);
+ return hash1 + (hash2 >> 16);
+}
+
+// Paul Hsieh hash
+#define stb__get16(p) ((p)[0] | ((p)[1] << 8))
+
+unsigned int stb_hash_fast(void *p, int len)
+{
+ unsigned char *q = (unsigned char *) p;
+ unsigned int hash = len;
+
+ if (len <= 0 || q == NULL) return 0;
+
+ /* Main loop */
+ for (;len > 3; len -= 4) {
+ unsigned int val;
+ hash += stb__get16(q);
+ val = (stb__get16(q+2) << 11);
+ hash = (hash << 16) ^ hash ^ val;
+ q += 4;
+ hash += hash >> 11;
+ }
+
+ /* Handle end cases */
+ switch (len) {
+ case 3: hash += stb__get16(q);
+ hash ^= hash << 16;
+ hash ^= q[2] << 18;
+ hash += hash >> 11;
+ break;
+ case 2: hash += stb__get16(q);
+ hash ^= hash << 11;
+ hash += hash >> 17;
+ break;
+ case 1: hash += q[0];
+ hash ^= hash << 10;
+ hash += hash >> 1;
+ break;
+ case 0: break;
+ }
+
+ /* Force "avalanching" of final 127 bits */
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+
+ return hash;
+}
+
+unsigned int stb_hash_number(unsigned int hash)
+{
+ hash ^= hash << 3;
+ hash += hash >> 5;
+ hash ^= hash << 4;
+ hash += hash >> 17;
+ hash ^= hash << 25;
+ hash += hash >> 6;
+ return hash;
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Perfect hashing for ints/pointers
+//
+// This is mainly useful for making faster pointer-indexed tables
+// that don't change frequently. E.g. for stb_ischar().
+//
+
+typedef struct
+{
+ stb_uint32 addend;
+ stb_uint multiplicand;
+ stb_uint b_mask;
+ stb_uint8 small_bmap[16];
+ stb_uint16 *large_bmap;
+
+ stb_uint table_mask;
+ stb_uint32 *table;
+} stb_perfect;
+
+STB_EXTERN int stb_perfect_create(stb_perfect *,unsigned int*,int n);
+STB_EXTERN void stb_perfect_destroy(stb_perfect *);
+STB_EXTERN int stb_perfect_hash(stb_perfect *, unsigned int x);
+extern int stb_perfect_hash_max_failures;
+
+#ifdef STB_DEFINE
+
+int stb_perfect_hash_max_failures;
+
+int stb_perfect_hash(stb_perfect *p, unsigned int x)
+{
+ stb_uint m = x * p->multiplicand;
+ stb_uint y = x >> 16;
+ stb_uint bv = (m >> 24) + y;
+ stb_uint av = (m + y) >> 12;
+ if (p->table == NULL) return -1; // uninitialized table fails
+ bv &= p->b_mask;
+ av &= p->table_mask;
+ if (p->large_bmap)
+ av ^= p->large_bmap[bv];
+ else
+ av ^= p->small_bmap[bv];
+ return p->table[av] == x ? av : -1;
+}
+
+static void stb__perfect_prehash(stb_perfect *p, stb_uint x, stb_uint16 *a, stb_uint16 *b)
+{
+ stb_uint m = x * p->multiplicand;
+ stb_uint y = x >> 16;
+ stb_uint bv = (m >> 24) + y;
+ stb_uint av = (m + y) >> 12;
+ bv &= p->b_mask;
+ av &= p->table_mask;
+ *b = bv;
+ *a = av;
+}
+
+static unsigned long stb__perfect_rand(void)
+{
+ static unsigned long stb__rand;
+ stb__rand = stb__rand * 2147001325 + 715136305;
+ return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16));
+}
+
+typedef struct {
+ unsigned short count;
+ unsigned short b;
+ unsigned short map;
+ unsigned short *entries;
+} stb__slot;
+
+static int stb__slot_compare(const void *p, const void *q)
+{
+ stb__slot *a = (stb__slot *) p;
+ stb__slot *b = (stb__slot *) q;
+ return a->count > b->count ? -1 : a->count < b->count; // sort large to small
+}
+
+int stb_perfect_create(stb_perfect *p, unsigned int *v, int n)
+{
+ unsigned int buffer1[64], buffer2[64], buffer3[64], buffer4[64], buffer5[32];
+ unsigned short *as = (unsigned short *) stb_temp(buffer1, sizeof(*v)*n);
+ unsigned short *bs = (unsigned short *) stb_temp(buffer2, sizeof(*v)*n);
+ unsigned short *entries = (unsigned short *) stb_temp(buffer4, sizeof(*entries) * n);
+ int size = 1 << stb_log2_ceil(n), bsize=8;
+ int failure = 0,i,j,k;
+
+ assert(n <= 32768);
+ p->large_bmap = NULL;
+
+ for(;;) {
+ stb__slot *bcount = (stb__slot *) stb_temp(buffer3, sizeof(*bcount) * bsize);
+ unsigned short *bloc = (unsigned short *) stb_temp(buffer5, sizeof(*bloc) * bsize);
+ unsigned short *e;
+ int bad=0;
+
+ p->addend = stb__perfect_rand();
+ p->multiplicand = stb__perfect_rand() | 1;
+ p->table_mask = size-1;
+ p->b_mask = bsize-1;
+ p->table = (stb_uint32 *) malloc(size * sizeof(*p->table));
+
+ for (i=0; i < bsize; ++i) {
+ bcount[i].b = i;
+ bcount[i].count = 0;
+ bcount[i].map = 0;
+ }
+ for (i=0; i < n; ++i) {
+ stb__perfect_prehash(p, v[i], as+i, bs+i);
+ ++bcount[bs[i]].count;
+ }
+ qsort(bcount, bsize, sizeof(*bcount), stb__slot_compare);
+ e = entries; // now setup up their entries index
+ for (i=0; i < bsize; ++i) {
+ bcount[i].entries = e;
+ e += bcount[i].count;
+ bcount[i].count = 0;
+ bloc[bcount[i].b] = i;
+ }
+ // now fill them out
+ for (i=0; i < n; ++i) {
+ int b = bs[i];
+ int w = bloc[b];
+ bcount[w].entries[bcount[w].count++] = i;
+ }
+ stb_tempfree(buffer5,bloc);
+ // verify
+ for (i=0; i < bsize; ++i)
+ for (j=0; j < bcount[i].count; ++j)
+ assert(bs[bcount[i].entries[j]] == bcount[i].b);
+ memset(p->table, 0, size*sizeof(*p->table));
+
+ // check if any b has duplicate a
+ for (i=0; i < bsize; ++i) {
+ if (bcount[i].count > 1) {
+ for (j=0; j < bcount[i].count; ++j) {
+ if (p->table[as[bcount[i].entries[j]]])
+ bad = 1;
+ p->table[as[bcount[i].entries[j]]] = 1;
+ }
+ for (j=0; j < bcount[i].count; ++j) {
+ p->table[as[bcount[i].entries[j]]] = 0;
+ }
+ if (bad) break;
+ }
+ }
+
+ if (!bad) {
+ // go through the bs and populate the table, first fit
+ for (i=0; i < bsize; ++i) {
+ if (bcount[i].count) {
+ // go through the candidate table[b] values
+ for (j=0; j < size; ++j) {
+ // go through the a values and see if they fit
+ for (k=0; k < bcount[i].count; ++k) {
+ int a = as[bcount[i].entries[k]];
+ if (p->table[(a^j)&p->table_mask]) {
+ break; // fails
+ }
+ }
+ // if succeeded, accept
+ if (k == bcount[i].count) {
+ bcount[i].map = j;
+ for (k=0; k < bcount[i].count; ++k) {
+ int a = as[bcount[i].entries[k]];
+ p->table[(a^j)&p->table_mask] = 1;
+ }
+ break;
+ }
+ }
+ if (j == size)
+ break; // no match for i'th entry, so break out in failure
+ }
+ }
+ if (i == bsize) {
+ // success... fill out map
+ if (bsize <= 16 && size <= 256) {
+ p->large_bmap = NULL;
+ for (i=0; i < bsize; ++i)
+ p->small_bmap[bcount[i].b] = (stb_uint8) bcount[i].map;
+ } else {
+ p->large_bmap = (unsigned short *) malloc(sizeof(*p->large_bmap) * bsize);
+ for (i=0; i < bsize; ++i)
+ p->large_bmap[bcount[i].b] = bcount[i].map;
+ }
+
+ // initialize table to v[0], so empty slots will fail
+ for (i=0; i < size; ++i)
+ p->table[i] = v[0];
+
+ for (i=0; i < n; ++i)
+ if (p->large_bmap)
+ p->table[as[i] ^ p->large_bmap[bs[i]]] = v[i];
+ else
+ p->table[as[i] ^ p->small_bmap[bs[i]]] = v[i];
+
+ // and now validate that none of them collided
+ for (i=0; i < n; ++i)
+ assert(stb_perfect_hash(p, v[i]) >= 0);
+
+ stb_tempfree(buffer3, bcount);
+ break;
+ }
+ }
+ free(p->table);
+ p->table = NULL;
+ stb_tempfree(buffer3, bcount);
+
+ ++failure;
+ if (failure >= 4 && bsize < size) bsize *= 2;
+ if (failure >= 8 && (failure & 3) == 0 && size < 4*n) {
+ size *= 2;
+ bsize *= 2;
+ }
+ if (failure == 6) {
+ // make sure the input data is unique, so we don't infinite loop
+ unsigned int *data = (unsigned int *) stb_temp(buffer3, n * sizeof(*data));
+ memcpy(data, v, sizeof(*data) * n);
+ qsort(data, n, sizeof(*data), stb_intcmp(0));
+ for (i=1; i < n; ++i) {
+ if (data[i] == data[i-1])
+ size = 0; // size is return value, so 0 it
+ }
+ stb_tempfree(buffer3, data);
+ if (!size) break;
+ }
+ }
+
+ if (failure > stb_perfect_hash_max_failures)
+ stb_perfect_hash_max_failures = failure;
+
+ stb_tempfree(buffer1, as);
+ stb_tempfree(buffer2, bs);
+ stb_tempfree(buffer4, entries);
+
+ return size;
+}
+
+void stb_perfect_destroy(stb_perfect *p)
+{
+ if (p->large_bmap) free(p->large_bmap);
+ if (p->table ) free(p->table);
+ p->large_bmap = NULL;
+ p->table = NULL;
+ p->b_mask = 0;
+ p->table_mask = 0;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Perfect hash clients
+
+STB_EXTERN int stb_ischar(char s, char *set);
+
+#ifdef STB_DEFINE
+
+int stb_ischar(char c, char *set)
+{
+ static unsigned char bit[8] = { 1,2,4,8,16,32,64,128 };
+ static stb_perfect p;
+ static unsigned char (*tables)[256];
+ static char ** sets = NULL;
+
+ int z = stb_perfect_hash(&p, (int)(size_t) set);
+ if (z < 0) {
+ int i,k,n,j,f;
+ // special code that means free all existing data
+ if (set == NULL) {
+ stb_arr_free(sets);
+ free(tables);
+ tables = NULL;
+ stb_perfect_destroy(&p);
+ return 0;
+ }
+ stb_arr_push(sets, set);
+ stb_perfect_destroy(&p);
+ n = stb_perfect_create(&p, (unsigned int *) (char **) sets, stb_arr_len(sets));
+ assert(n != 0);
+ k = (n+7) >> 3;
+ tables = (unsigned char (*)[256]) realloc(tables, sizeof(*tables) * k);
+ memset(tables, 0, sizeof(*tables) * k);
+ for (i=0; i < stb_arr_len(sets); ++i) {
+ k = stb_perfect_hash(&p, (int)(size_t) sets[i]);
+ assert(k >= 0);
+ n = k >> 3;
+ f = bit[k&7];
+ for (j=0; !j || sets[i][j]; ++j) {
+ tables[n][(unsigned char) sets[i][j]] |= f;
+ }
+ }
+ z = stb_perfect_hash(&p, (int)(size_t) set);
+ }
+ return tables[z >> 3][(unsigned char) c] & bit[z & 7];
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Instantiated data structures
+//
+// This is an attempt to implement a templated data structure.
+//
+// Hash table: call stb_define_hash(TYPE,N,KEY,K1,K2,HASH,VALUE)
+// TYPE -- will define a structure type containing the hash table
+// N -- the name, will prefix functions named:
+// N create
+// N destroy
+// N get
+// N set, N add, N update,
+// N remove
+// KEY -- the type of the key. 'x == y' must be valid
+// K1,K2 -- keys never used by the app, used as flags in the hashtable
+// HASH -- a piece of code ending with 'return' that hashes key 'k'
+// VALUE -- the type of the value. 'x = y' must be valid
+//
+// Note that stb_define_hash_base can be used to define more sophisticated
+// hash tables, e.g. those that make copies of the key or use special
+// comparisons (e.g. strcmp).
+
+#define STB_(prefix,name) stb__##prefix##name
+#define STB__(prefix,name) prefix##name
+#define STB__use(x) x
+#define STB__skip(x)
+
+#define stb_declare_hash(PREFIX,TYPE,N,KEY,VALUE) \
+ typedef struct stb__st_##TYPE TYPE;\
+ PREFIX int STB__(N, init)(TYPE *h, int count);\
+ PREFIX int STB__(N, memory_usage)(TYPE *h);\
+ PREFIX TYPE * STB__(N, create)(void);\
+ PREFIX TYPE * STB__(N, copy)(TYPE *h);\
+ PREFIX void STB__(N, destroy)(TYPE *h);\
+ PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v);\
+ PREFIX VALUE STB__(N,get)(TYPE *a, KEY k);\
+ PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v);\
+ PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v);\
+ PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v);\
+ PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v);
+
+#define STB_nocopy(x) (x)
+#define STB_nodelete(x) 0
+#define STB_nofields
+#define STB_nonullvalue(x)
+#define STB_nullvalue(x) x
+#define STB_safecompare(x) x
+#define STB_nosafe(x)
+#define STB_noprefix
+
+#ifdef __GNUC__
+#define STB__nogcc(x)
+#else
+#define STB__nogcc(x) x
+#endif
+
+#define stb_define_hash_base(PREFIX,TYPE,FIELDS,N,NC,LOAD_FACTOR, \
+ KEY,EMPTY,DEL,COPY,DISPOSE,SAFE, \
+ VCOMPARE,CCOMPARE,HASH, \
+ VALUE,HASVNULL,VNULL) \
+ \
+typedef struct \
+{ \
+ KEY k; \
+ VALUE v; \
+} STB_(N,_hashpair); \
+ \
+STB__nogcc( typedef struct stb__st_##TYPE TYPE; ) \
+struct stb__st_##TYPE { \
+ FIELDS \
+ STB_(N,_hashpair) *table; \
+ unsigned int mask; \
+ int count, limit; \
+ int deleted; \
+ \
+ int delete_threshhold; \
+ int grow_threshhold; \
+ int shrink_threshhold; \
+ unsigned char alloced, has_empty, has_del; \
+ VALUE ev; VALUE dv; \
+}; \
+ \
+static unsigned int STB_(N, hash)(KEY k) \
+{ \
+ HASH \
+} \
+ \
+PREFIX int STB__(N, init)(TYPE *h, int count) \
+{ \
+ int i; \
+ if (count < 4) count = 4; \
+ h->limit = count; \
+ h->count = 0; \
+ h->mask = count-1; \
+ h->deleted = 0; \
+ h->grow_threshhold = (int) (count * LOAD_FACTOR); \
+ h->has_empty = h->has_del = 0; \
+ h->alloced = 0; \
+ if (count <= 64) \
+ h->shrink_threshhold = 0; \
+ else \
+ h->shrink_threshhold = (int) (count * (LOAD_FACTOR/2.25)); \
+ h->delete_threshhold = (int) (count * (1-LOAD_FACTOR)/2); \
+ h->table = (STB_(N,_hashpair)*) malloc(sizeof(h->table[0]) * count); \
+ if (h->table == NULL) return 0; \
+ /* ideally this gets turned into a memset32 automatically */ \
+ for (i=0; i < count; ++i) \
+ h->table[i].k = EMPTY; \
+ return 1; \
+} \
+ \
+PREFIX int STB__(N, memory_usage)(TYPE *h) \
+{ \
+ return sizeof(*h) + h->limit * sizeof(h->table[0]); \
+} \
+ \
+PREFIX TYPE * STB__(N, create)(void) \
+{ \
+ TYPE *h = (TYPE *) malloc(sizeof(*h)); \
+ if (h) { \
+ if (STB__(N, init)(h, 16)) \
+ h->alloced = 1; \
+ else { free(h); h=NULL; } \
+ } \
+ return h; \
+} \
+ \
+PREFIX void STB__(N, destroy)(TYPE *a) \
+{ \
+ int i; \
+ for (i=0; i < a->limit; ++i) \
+ if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k, DEL)) \
+ DISPOSE(a->table[i].k); \
+ free(a->table); \
+ if (a->alloced) \
+ free(a); \
+} \
+ \
+static void STB_(N, rehash)(TYPE *a, int count); \
+ \
+PREFIX int STB__(N,get_flag)(TYPE *a, KEY k, VALUE *v) \
+{ \
+ unsigned int h = STB_(N, hash)(k); \
+ unsigned int n = h & a->mask, s; \
+ if (CCOMPARE(k,EMPTY)){ if (a->has_empty) *v = a->ev; return a->has_empty;}\
+ if (CCOMPARE(k,DEL)) { if (a->has_del ) *v = a->dv; return a->has_del; }\
+ if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \
+ SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \
+ if (VCOMPARE(a->table[n].k,k)) { *v = a->table[n].v; return 1; } \
+ s = stb_rehash(h) | 1; \
+ for(;;) { \
+ n = (n + s) & a->mask; \
+ if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \
+ SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \
+ if (VCOMPARE(a->table[n].k,k)) \
+ { *v = a->table[n].v; return 1; } \
+ } \
+} \
+ \
+HASVNULL( \
+ PREFIX VALUE STB__(N,get)(TYPE *a, KEY k) \
+ { \
+ VALUE v; \
+ if (STB__(N,get_flag)(a,k,&v)) return v; \
+ else return VNULL; \
+ } \
+) \
+ \
+PREFIX int STB__(N,getkey)(TYPE *a, KEY k, KEY *kout) \
+{ \
+ unsigned int h = STB_(N, hash)(k); \
+ unsigned int n = h & a->mask, s; \
+ if (CCOMPARE(k,EMPTY)||CCOMPARE(k,DEL)) return 0; \
+ if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \
+ SAFE(if (!CCOMPARE(a->table[n].k,DEL))) \
+ if (VCOMPARE(a->table[n].k,k)) { *kout = a->table[n].k; return 1; } \
+ s = stb_rehash(h) | 1; \
+ for(;;) { \
+ n = (n + s) & a->mask; \
+ if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \
+ SAFE(if (CCOMPARE(a->table[n].k,DEL)) continue;) \
+ if (VCOMPARE(a->table[n].k,k)) \
+ { *kout = a->table[n].k; return 1; } \
+ } \
+} \
+ \
+static int STB_(N,addset)(TYPE *a, KEY k, VALUE v, \
+ int allow_new, int allow_old, int copy) \
+{ \
+ unsigned int h = STB_(N, hash)(k); \
+ unsigned int n = h & a->mask; \
+ int b = -1; \
+ if (CCOMPARE(k,EMPTY)) { \
+ if (a->has_empty ? allow_old : allow_new) { \
+ n=a->has_empty; a->ev = v; a->has_empty = 1; return !n; \
+ } else return 0; \
+ } \
+ if (CCOMPARE(k,DEL)) { \
+ if (a->has_del ? allow_old : allow_new) { \
+ n=a->has_del; a->dv = v; a->has_del = 1; return !n; \
+ } else return 0; \
+ } \
+ if (!CCOMPARE(a->table[n].k, EMPTY)) { \
+ unsigned int s; \
+ if (CCOMPARE(a->table[n].k, DEL)) \
+ b = n; \
+ else if (VCOMPARE(a->table[n].k,k)) { \
+ if (allow_old) \
+ a->table[n].v = v; \
+ return !allow_new; \
+ } \
+ s = stb_rehash(h) | 1; \
+ for(;;) { \
+ n = (n + s) & a->mask; \
+ if (CCOMPARE(a->table[n].k, EMPTY)) break; \
+ if (CCOMPARE(a->table[n].k, DEL)) { \
+ if (b < 0) b = n; \
+ } else if (VCOMPARE(a->table[n].k,k)) { \
+ if (allow_old) \
+ a->table[n].v = v; \
+ return !allow_new; \
+ } \
+ } \
+ } \
+ if (!allow_new) return 0; \
+ if (b < 0) b = n; else --a->deleted; \
+ a->table[b].k = copy ? COPY(k) : k; \
+ a->table[b].v = v; \
+ ++a->count; \
+ if (a->count > a->grow_threshhold) \
+ STB_(N,rehash)(a, a->limit*2); \
+ return 1; \
+} \
+ \
+PREFIX int STB__(N, set)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,1,1);}\
+PREFIX int STB__(N, add)(TYPE *a, KEY k, VALUE v){return STB_(N,addset)(a,k,v,1,0,1);}\
+PREFIX int STB__(N, update)(TYPE*a,KEY k,VALUE v){return STB_(N,addset)(a,k,v,0,1,1);}\
+ \
+PREFIX int STB__(N, remove)(TYPE *a, KEY k, VALUE *v) \
+{ \
+ unsigned int h = STB_(N, hash)(k); \
+ unsigned int n = h & a->mask, s; \
+ if (CCOMPARE(k,EMPTY)) { if (a->has_empty) { if(v)*v = a->ev; a->has_empty=0; return 1; } return 0; } \
+ if (CCOMPARE(k,DEL)) { if (a->has_del ) { if(v)*v = a->dv; a->has_del =0; return 1; } return 0; } \
+ if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \
+ if (SAFE(CCOMPARE(a->table[n].k,DEL) || ) !VCOMPARE(a->table[n].k,k)) { \
+ s = stb_rehash(h) | 1; \
+ for(;;) { \
+ n = (n + s) & a->mask; \
+ if (CCOMPARE(a->table[n].k,EMPTY)) return 0; \
+ SAFE(if (CCOMPARE(a->table[n].k, DEL)) continue;) \
+ if (VCOMPARE(a->table[n].k,k)) break; \
+ } \
+ } \
+ DISPOSE(a->table[n].k); \
+ a->table[n].k = DEL; \
+ --a->count; \
+ ++a->deleted; \
+ if (v != NULL) \
+ *v = a->table[n].v; \
+ if (a->count < a->shrink_threshhold) \
+ STB_(N, rehash)(a, a->limit >> 1); \
+ else if (a->deleted > a->delete_threshhold) \
+ STB_(N, rehash)(a, a->limit); \
+ return 1; \
+} \
+ \
+PREFIX TYPE * STB__(NC, copy)(TYPE *a) \
+{ \
+ int i; \
+ TYPE *h = (TYPE *) malloc(sizeof(*h)); \
+ if (!h) return NULL; \
+ if (!STB__(N, init)(h, a->limit)) { free(h); return NULL; } \
+ h->count = a->count; \
+ h->deleted = a->deleted; \
+ h->alloced = 1; \
+ h->ev = a->ev; h->dv = a->dv; \
+ h->has_empty = a->has_empty; h->has_del = a->has_del; \
+ memcpy(h->table, a->table, h->limit * sizeof(h->table[0])); \
+ for (i=0; i < a->limit; ++i) \
+ if (!CCOMPARE(h->table[i].k,EMPTY) && !CCOMPARE(h->table[i].k,DEL)) \
+ h->table[i].k = COPY(h->table[i].k); \
+ return h; \
+} \
+ \
+static void STB_(N, rehash)(TYPE *a, int count) \
+{ \
+ int i; \
+ TYPE b; \
+ STB__(N, init)(&b, count); \
+ for (i=0; i < a->limit; ++i) \
+ if (!CCOMPARE(a->table[i].k,EMPTY) && !CCOMPARE(a->table[i].k,DEL)) \
+ STB_(N,addset)(&b, a->table[i].k, a->table[i].v,1,1,0); \
+ free(a->table); \
+ a->table = b.table; \
+ a->mask = b.mask; \
+ a->count = b.count; \
+ a->limit = b.limit; \
+ a->deleted = b.deleted; \
+ a->delete_threshhold = b.delete_threshhold; \
+ a->grow_threshhold = b.grow_threshhold; \
+ a->shrink_threshhold = b.shrink_threshhold; \
+}
+
+#define STB_equal(a,b) ((a) == (b))
+
+#define stb_define_hash(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE) \
+ stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \
+ KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \
+ STB_equal,STB_equal,HASH, \
+ VALUE,STB_nonullvalue,0)
+
+#define stb_define_hash_vnull(TYPE,N,KEY,EMPTY,DEL,HASH,VALUE,VNULL) \
+ stb_define_hash_base(STB_noprefix, TYPE,STB_nofields,N,NC,0.85f, \
+ KEY,EMPTY,DEL,STB_nocopy,STB_nodelete,STB_nosafe, \
+ STB_equal,STB_equal,HASH, \
+ VALUE,STB_nullvalue,VNULL)
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_ptrmap
+//
+// An stb_ptrmap data structure is an O(1) hash table between pointers. One
+// application is to let you store "extra" data associated with pointers,
+// which is why it was originally called stb_extra.
+
+stb_declare_hash(STB_EXTERN, stb_ptrmap, stb_ptrmap_, void *, void *)
+stb_declare_hash(STB_EXTERN, stb_idict, stb_idict_, stb_int32, stb_int32)
+stb_declare_hash(STB_EXTERN, stb_uidict, stbi_uidict_, stb_uint32, stb_uint32)
+
+STB_EXTERN void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *));
+STB_EXTERN stb_ptrmap *stb_ptrmap_new(void);
+
+STB_EXTERN stb_idict * stb_idict_new_size(int size);
+STB_EXTERN void stb_idict_remove_all(stb_idict *e);
+STB_EXTERN void stb_uidict_reset(stb_uidict *e);
+
+#ifdef STB_DEFINE
+
+#define STB_EMPTY ((void *) 2)
+#define STB_EDEL ((void *) 6)
+
+stb_define_hash_base(STB_noprefix,stb_ptrmap, STB_nofields, stb_ptrmap_,stb_ptrmap_,0.85f,
+ void *,STB_EMPTY,STB_EDEL,STB_nocopy,STB_nodelete,STB_nosafe,
+ STB_equal,STB_equal,return stb_hashptr(k);,
+ void *,STB_nullvalue,NULL)
+
+stb_ptrmap *stb_ptrmap_new(void)
+{
+ return stb_ptrmap_create();
+}
+
+void stb_ptrmap_delete(stb_ptrmap *e, void (*free_func)(void *))
+{
+ int i;
+ if (free_func)
+ for (i=0; i < e->limit; ++i)
+ if (e->table[i].k != STB_EMPTY && e->table[i].k != STB_EDEL) {
+ if (free_func == free)
+ free(e->table[i].v); // allow STB_MALLOC_WRAPPER to operate
+ else
+ free_func(e->table[i].v);
+ }
+ stb_ptrmap_destroy(e);
+}
+
+// extra fields needed for stua_dict
+#define STB_IEMPTY ((int) 1)
+#define STB_IDEL ((int) 3)
+stb_define_hash_base(STB_noprefix, stb_idict, short type; short gc; STB_nofields, stb_idict_,stb_idict_,0.95f,
+ stb_int32,STB_IEMPTY,STB_IDEL,STB_nocopy,STB_nodelete,STB_nosafe,
+ STB_equal,STB_equal,
+ return stb_rehash_improved(k);,stb_int32,STB_nonullvalue,0)
+
+stb_idict * stb_idict_new_size(int size)
+{
+ stb_idict *e = (stb_idict *) malloc(sizeof(*e));
+ if (e) {
+ if (!stb_is_pow2(size))
+ size = 1 << stb_log2_ceil(size);
+ stb_idict_init(e, size);
+ e->alloced = 1;
+ }
+ return e;
+}
+
+void stb_idict_remove_all(stb_idict *e)
+{
+ int n;
+ for (n=0; n < e->limit; ++n)
+ e->table[n].k = STB_IEMPTY;
+ e->has_empty = e->has_del = 0;
+ e->count = 0;
+ e->deleted = 0;
+}
+
+stb_define_hash_base(STB_noprefix, stb_uidict, STB_nofields, stb_uidict_,stb_uidict_,0.85f,
+ stb_int32,0xffffffff,0xfffffffe,STB_nocopy,STB_nodelete,STB_nosafe,
+ STB_equal,STB_equal,
+ return stb_rehash_improved(k);,stb_uint32,STB_nonullvalue,0)
+
+void stb_uidict_reset(stb_uidict *e)
+{
+ int n;
+ for (n=0; n < e->limit; ++n)
+ e->table[n].k = 0xffffffff;
+ e->has_empty = e->has_del = 0;
+ e->count = 0;
+ e->deleted = 0;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_sparse_ptr_matrix
+//
+// An stb_ptrmap data structure is an O(1) hash table storing an arbitrary
+// block of data for a given pair of pointers.
+//
+// If create=0, returns
+
+typedef struct stb__st_stb_spmatrix stb_spmatrix;
+
+STB_EXTERN stb_spmatrix * stb_sparse_ptr_matrix_new(int val_size);
+STB_EXTERN void stb_sparse_ptr_matrix_free(stb_spmatrix *z);
+STB_EXTERN void * stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create);
+
+#ifdef STB_DEFINE
+typedef struct
+{
+ void *a;
+ void *b;
+} stb__ptrpair;
+
+static stb__ptrpair stb__ptrpair_empty = { (void *) 1, (void *) 1 };
+static stb__ptrpair stb__ptrpair_del = { (void *) 2, (void *) 2 };
+
+#define STB__equal_ptrpair(x,y) ((x).a == (y).a && (x).b == (y).b)
+
+stb_define_hash_base(STB_noprefix, stb_spmatrix, int val_size; void *arena;, stb__spmatrix_,stb__spmatrix_, 0.85,
+ stb__ptrpair, stb__ptrpair_empty, stb__ptrpair_del,
+ STB_nocopy, STB_nodelete, STB_nosafe,
+ STB__equal_ptrpair, STB__equal_ptrpair, return stb_rehash(stb_hashptr(k.a))+stb_hashptr(k.b);,
+ void *, STB_nullvalue, 0)
+
+stb_spmatrix *stb_sparse_ptr_matrix_new(int val_size)
+{
+ stb_spmatrix *m = stb__spmatrix_create();
+ if (m) m->val_size = val_size;
+ if (m) m->arena = stb_malloc_global(1);
+ return m;
+}
+
+void stb_sparse_ptr_matrix_free(stb_spmatrix *z)
+{
+ if (z->arena) stb_free(z->arena);
+ stb__spmatrix_destroy(z);
+}
+
+void *stb_sparse_ptr_matrix_get(stb_spmatrix *z, void *a, void *b, int create)
+{
+ stb__ptrpair t = { a,b };
+ void *data = stb__spmatrix_get(z, t);
+ if (!data && create) {
+ data = stb_malloc_raw(z->arena, z->val_size);
+ if (!data) return NULL;
+ memset(data, 0, z->val_size);
+ stb__spmatrix_add(z, t, data);
+ }
+ return data;
+}
+#endif
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// SDICT: Hash Table for Strings (symbol table)
+//
+// if "use_arena=1", then strings will be copied
+// into blocks and never freed until the sdict is freed;
+// otherwise they're malloc()ed and free()d on the fly.
+// (specify use_arena=1 if you never stb_sdict_remove)
+
+stb_declare_hash(STB_EXTERN, stb_sdict, stb_sdict_, char *, void *)
+
+STB_EXTERN stb_sdict * stb_sdict_new(int use_arena);
+STB_EXTERN stb_sdict * stb_sdict_copy(stb_sdict*);
+STB_EXTERN void stb_sdict_delete(stb_sdict *);
+STB_EXTERN void * stb_sdict_change(stb_sdict *, char *str, void *p);
+STB_EXTERN int stb_sdict_count(stb_sdict *d);
+
+STB_EXTERN int stb_sdict_internal_limit(stb_sdict *d);
+STB_EXTERN char * stb_sdict_internal_key(stb_sdict *d, int n);
+STB_EXTERN void * stb_sdict_internal_value(stb_sdict *d, int n);
+
+#define stb_sdict_for(d,i,q,z) \
+ for(i=0; i < stb_sdict_internal_limit(d) ? (q=stb_sdict_internal_key(d,i),z=stb_sdict_internal_value(d,i),1) : 0; ++i) \
+ if (q==NULL||q==(void *) 1);else // reversed makes macro friendly
+
+#ifdef STB_DEFINE
+
+// if in same translation unit, for speed, don't call accessors
+#undef stb_sdict_for
+#define stb_sdict_for(d,i,q,z) \
+ for(i=0; i < (d)->limit ? (q=(d)->table[i].k,z=(d)->table[i].v,1) : 0; ++i) \
+ if (q==NULL||q==(void *) 1);else // reversed makes macro friendly
+
+#define STB_DEL ((void *) 1)
+#define STB_SDEL ((char *) 1)
+
+#define stb_sdict__copy(x) \
+ stb_p_strcpy_s(a->arena ? stb_malloc_string(a->arena, strlen(x)+1) \
+ : (char *) malloc(strlen(x)+1), strlen(x)+1, x)
+
+#define stb_sdict__dispose(x) if (!a->arena) free(x)
+
+stb_define_hash_base(STB_noprefix, stb_sdict, void*arena;, stb_sdict_,stb_sdictinternal_, 0.85f,
+ char *, NULL, STB_SDEL, stb_sdict__copy, stb_sdict__dispose,
+ STB_safecompare, !strcmp, STB_equal, return stb_hash(k);,
+ void *, STB_nullvalue, NULL)
+
+int stb_sdict_count(stb_sdict *a)
+{
+ return a->count;
+}
+
+int stb_sdict_internal_limit(stb_sdict *a)
+{
+ return a->limit;
+}
+char* stb_sdict_internal_key(stb_sdict *a, int n)
+{
+ return a->table[n].k;
+}
+void* stb_sdict_internal_value(stb_sdict *a, int n)
+{
+ return a->table[n].v;
+}
+
+stb_sdict * stb_sdict_new(int use_arena)
+{
+ stb_sdict *d = stb_sdict_create();
+ if (d == NULL) return NULL;
+ d->arena = use_arena ? stb_malloc_global(1) : NULL;
+ return d;
+}
+
+stb_sdict* stb_sdict_copy(stb_sdict *old)
+{
+ stb_sdict *n;
+ void *old_arena = old->arena;
+ void *new_arena = old_arena ? stb_malloc_global(1) : NULL;
+ old->arena = new_arena;
+ n = stb_sdictinternal_copy(old);
+ old->arena = old_arena;
+ if (n)
+ n->arena = new_arena;
+ else if (new_arena)
+ stb_free(new_arena);
+ return n;
+}
+
+
+void stb_sdict_delete(stb_sdict *d)
+{
+ if (d->arena)
+ stb_free(d->arena);
+ stb_sdict_destroy(d);
+}
+
+void * stb_sdict_change(stb_sdict *d, char *str, void *p)
+{
+ void *q = stb_sdict_get(d, str);
+ stb_sdict_set(d, str, p);
+ return q;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Instantiated data structures
+//
+// This is an attempt to implement a templated data structure.
+// What you do is define a struct foo, and then include several
+// pointer fields to struct foo in your struct. Then you call
+// the instantiator, which creates the functions that implement
+// the data structure. This requires massive undebuggable #defines,
+// so we limit the cases where we do this.
+//
+// AA tree is an encoding of a 2-3 tree whereas RB trees encode a 2-3-4 tree;
+// much simpler code due to fewer cases.
+
+#define stb__bst_parent(x) x
+#define stb__bst_noparent(x)
+
+#define stb_bst_fields(N) \
+ *STB_(N,left), *STB_(N,right); \
+ unsigned char STB_(N,level)
+
+#define stb_bst_fields_parent(N) \
+ *STB_(N,left), *STB_(N,right), *STB_(N,parent); \
+ unsigned char STB_(N,level)
+
+#define STB__level(N,x) ((x) ? (x)->STB_(N,level) : 0)
+
+#define stb_bst_base(TYPE, N, TREE, M, compare, PAR) \
+ \
+static int STB_(N,_compare)(TYPE *p, TYPE *q) \
+{ \
+ compare \
+} \
+ \
+static void STB_(N,setleft)(TYPE *q, TYPE *v) \
+{ \
+ q->STB_(N,left) = v; \
+ PAR(if (v) v->STB_(N,parent) = q;) \
+} \
+ \
+static void STB_(N,setright)(TYPE *q, TYPE *v) \
+{ \
+ q->STB_(N,right) = v; \
+ PAR(if (v) v->STB_(N,parent) = q;) \
+} \
+ \
+static TYPE *STB_(N,skew)(TYPE *q) \
+{ \
+ if (q == NULL) return q; \
+ if (q->STB_(N,left) \
+ && q->STB_(N,left)->STB_(N,level) == q->STB_(N,level)) { \
+ TYPE *p = q->STB_(N,left); \
+ STB_(N,setleft)(q, p->STB_(N,right)); \
+ STB_(N,setright)(p, q); \
+ return p; \
+ } \
+ return q; \
+} \
+ \
+static TYPE *STB_(N,split)(TYPE *p) \
+{ \
+ TYPE *q = p->STB_(N,right); \
+ if (q && q->STB_(N,right) \
+ && q->STB_(N,right)->STB_(N,level) == p->STB_(N,level)) { \
+ STB_(N,setright)(p, q->STB_(N,left)); \
+ STB_(N,setleft)(q,p); \
+ ++q->STB_(N,level); \
+ return q; \
+ } \
+ return p; \
+} \
+ \
+TYPE *STB__(N,insert)(TYPE *tree, TYPE *item) \
+{ \
+ int c; \
+ if (tree == NULL) { \
+ item->STB_(N,left) = NULL; \
+ item->STB_(N,right) = NULL; \
+ item->STB_(N,level) = 1; \
+ PAR(item->STB_(N,parent) = NULL;) \
+ return item; \
+ } \
+ c = STB_(N,_compare)(item,tree); \
+ if (c == 0) { \
+ if (item != tree) { \
+ STB_(N,setleft)(item, tree->STB_(N,left)); \
+ STB_(N,setright)(item, tree->STB_(N,right)); \
+ item->STB_(N,level) = tree->STB_(N,level); \
+ PAR(item->STB_(N,parent) = NULL;) \
+ } \
+ return item; \
+ } \
+ if (c < 0) \
+ STB_(N,setleft )(tree, STB__(N,insert)(tree->STB_(N,left), item)); \
+ else \
+ STB_(N,setright)(tree, STB__(N,insert)(tree->STB_(N,right), item)); \
+ tree = STB_(N,skew)(tree); \
+ tree = STB_(N,split)(tree); \
+ PAR(tree->STB_(N,parent) = NULL;) \
+ return tree; \
+} \
+ \
+TYPE *STB__(N,remove)(TYPE *tree, TYPE *item) \
+{ \
+ static TYPE *delnode, *leaf, *restore; \
+ if (tree == NULL) return NULL; \
+ leaf = tree; \
+ if (STB_(N,_compare)(item, tree) < 0) { \
+ STB_(N,setleft)(tree, STB__(N,remove)(tree->STB_(N,left), item)); \
+ } else { \
+ TYPE *r; \
+ delnode = tree; \
+ r = STB__(N,remove)(tree->STB_(N,right), item); \
+ /* maybe move 'leaf' up to this location */ \
+ if (restore == tree) { tree = leaf; leaf = restore = NULL; } \
+ STB_(N,setright)(tree,r); \
+ assert(tree->STB_(N,right) != tree); \
+ } \
+ if (tree == leaf) { \
+ if (delnode == item) { \
+ tree = tree->STB_(N,right); \
+ assert(leaf->STB_(N,left) == NULL); \
+ /* move leaf (the right sibling) up to delnode */ \
+ STB_(N,setleft )(leaf, item->STB_(N,left )); \
+ STB_(N,setright)(leaf, item->STB_(N,right)); \
+ leaf->STB_(N,level) = item->STB_(N,level); \
+ if (leaf != item) \
+ restore = delnode; \
+ } \
+ delnode = NULL; \
+ } else { \
+ if (STB__level(N,tree->STB_(N,left) ) < tree->STB_(N,level)-1 || \
+ STB__level(N,tree->STB_(N,right)) < tree->STB_(N,level)-1) { \
+ --tree->STB_(N,level); \
+ if (STB__level(N,tree->STB_(N,right)) > tree->STB_(N,level)) \
+ tree->STB_(N,right)->STB_(N,level) = tree->STB_(N,level); \
+ tree = STB_(N,skew)(tree); \
+ STB_(N,setright)(tree, STB_(N,skew)(tree->STB_(N,right))); \
+ if (tree->STB_(N,right)) \
+ STB_(N,setright)(tree->STB_(N,right), \
+ STB_(N,skew)(tree->STB_(N,right)->STB_(N,right))); \
+ tree = STB_(N,split)(tree); \
+ if (tree->STB_(N,right)) \
+ STB_(N,setright)(tree, STB_(N,split)(tree->STB_(N,right))); \
+ } \
+ } \
+ PAR(if (tree) tree->STB_(N,parent) = NULL;) \
+ return tree; \
+} \
+ \
+TYPE *STB__(N,last)(TYPE *tree) \
+{ \
+ if (tree) \
+ while (tree->STB_(N,right)) tree = tree->STB_(N,right); \
+ return tree; \
+} \
+ \
+TYPE *STB__(N,first)(TYPE *tree) \
+{ \
+ if (tree) \
+ while (tree->STB_(N,left)) tree = tree->STB_(N,left); \
+ return tree; \
+} \
+ \
+TYPE *STB__(N,next)(TYPE *tree, TYPE *item) \
+{ \
+ TYPE *next = NULL; \
+ if (item->STB_(N,right)) \
+ return STB__(N,first)(item->STB_(N,right)); \
+ PAR( \
+ while(item->STB_(N,parent)) { \
+ TYPE *up = item->STB_(N,parent); \
+ if (up->STB_(N,left) == item) return up; \
+ item = up; \
+ } \
+ return NULL; \
+ ) \
+ while (tree != item) { \
+ if (STB_(N,_compare)(item, tree) < 0) { \
+ next = tree; \
+ tree = tree->STB_(N,left); \
+ } else { \
+ tree = tree->STB_(N,right); \
+ } \
+ } \
+ return next; \
+} \
+ \
+TYPE *STB__(N,prev)(TYPE *tree, TYPE *item) \
+{ \
+ TYPE *next = NULL; \
+ if (item->STB_(N,left)) \
+ return STB__(N,last)(item->STB_(N,left)); \
+ PAR( \
+ while(item->STB_(N,parent)) { \
+ TYPE *up = item->STB_(N,parent); \
+ if (up->STB_(N,right) == item) return up; \
+ item = up; \
+ } \
+ return NULL; \
+ ) \
+ while (tree != item) { \
+ if (STB_(N,_compare)(item, tree) < 0) { \
+ tree = tree->STB_(N,left); \
+ } else { \
+ next = tree; \
+ tree = tree->STB_(N,right); \
+ } \
+ } \
+ return next; \
+} \
+ \
+STB__DEBUG( \
+ void STB__(N,_validate)(TYPE *tree, int root) \
+ { \
+ if (tree == NULL) return; \
+ PAR(if(root) assert(tree->STB_(N,parent) == NULL);) \
+ assert(STB__level(N,tree->STB_(N,left) ) == tree->STB_(N,level)-1); \
+ assert(STB__level(N,tree->STB_(N,right)) <= tree->STB_(N,level)); \
+ assert(STB__level(N,tree->STB_(N,right)) >= tree->STB_(N,level)-1); \
+ if (tree->STB_(N,right)) { \
+ assert(STB__level(N,tree->STB_(N,right)->STB_(N,right)) \
+ != tree->STB_(N,level)); \
+ PAR(assert(tree->STB_(N,right)->STB_(N,parent) == tree);) \
+ } \
+ PAR(if(tree->STB_(N,left)) assert(tree->STB_(N,left)->STB_(N,parent) == tree);) \
+ STB__(N,_validate)(tree->STB_(N,left) ,0); \
+ STB__(N,_validate)(tree->STB_(N,right),0); \
+ } \
+) \
+ \
+typedef struct \
+{ \
+ TYPE *root; \
+} TREE; \
+ \
+void STB__(M,Insert)(TREE *tree, TYPE *item) \
+{ tree->root = STB__(N,insert)(tree->root, item); } \
+void STB__(M,Remove)(TREE *tree, TYPE *item) \
+{ tree->root = STB__(N,remove)(tree->root, item); } \
+TYPE *STB__(M,Next)(TREE *tree, TYPE *item) \
+{ return STB__(N,next)(tree->root, item); } \
+TYPE *STB__(M,Prev)(TREE *tree, TYPE *item) \
+{ return STB__(N,prev)(tree->root, item); } \
+TYPE *STB__(M,First)(TREE *tree) { return STB__(N,first)(tree->root); } \
+TYPE *STB__(M,Last) (TREE *tree) { return STB__(N,last) (tree->root); } \
+void STB__(M,Init)(TREE *tree) { tree->root = NULL; }
+
+
+#define stb_bst_find(N,tree,fcompare) \
+{ \
+ int c; \
+ while (tree != NULL) { \
+ fcompare \
+ if (c == 0) return tree; \
+ if (c < 0) tree = tree->STB_(N,left); \
+ else tree = tree->STB_(N,right); \
+ } \
+ return NULL; \
+}
+
+#define stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,PAR) \
+ stb_bst_base(TYPE,N,TREE,M, \
+ VTYPE a = p->vfield; VTYPE b = q->vfield; return (compare);, PAR ) \
+ \
+TYPE *STB__(N,find)(TYPE *tree, VTYPE a) \
+ stb_bst_find(N,tree,VTYPE b = tree->vfield; c = (compare);) \
+TYPE *STB__(M,Find)(TREE *tree, VTYPE a) \
+{ return STB__(N,find)(tree->root, a); }
+
+#define stb_bst(TYPE,N,TREE,M,vfield,VTYPE,compare) \
+ stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_noparent)
+#define stb_bst_parent(TYPE,N,TREE,M,vfield,VTYPE,compare) \
+ stb_bst_raw(TYPE,N,TREE,M,vfield,VTYPE,compare,stb__bst_parent)
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Pointer Nulling
+//
+// This lets you automatically NULL dangling pointers to "registered"
+// objects. Note that you have to make sure you call the appropriate
+// functions when you free or realloc blocks of memory that contain
+// pointers or pointer targets. stb.h can automatically do this for
+// stb_arr, or for all frees/reallocs if it's wrapping them.
+//
+
+#ifdef STB_NPTR
+
+STB_EXTERN void stb_nptr_set(void *address_of_pointer, void *value_to_write);
+STB_EXTERN void stb_nptr_didset(void *address_of_pointer);
+
+STB_EXTERN void stb_nptr_didfree(void *address_being_freed, int len);
+STB_EXTERN void stb_nptr_free(void *address_being_freed, int len);
+
+STB_EXTERN void stb_nptr_didrealloc(void *new_address, void *old_address, int len);
+STB_EXTERN void stb_nptr_recache(void); // recache all known pointers
+ // do this after pointer sets outside your control, slow
+
+#ifdef STB_DEFINE
+// for fast updating on free/realloc, we need to be able to find
+// all the objects (pointers and targets) within a given block;
+// this precludes hashing
+
+// we use a three-level hierarchy of memory to minimize storage:
+// level 1: 65536 pointers to stb__memory_node (always uses 256 KB)
+// level 2: each stb__memory_node represents a 64K block of memory
+// with 256 stb__memory_leafs (worst case 64MB)
+// level 3: each stb__memory_leaf represents 256 bytes of memory
+// using a list of target locations and a list of pointers
+// (which are hopefully fairly short normally!)
+
+// this approach won't work in 64-bit, which has a much larger address
+// space. need to redesign
+
+#define STB__NPTR_ROOT_LOG2 16
+#define STB__NPTR_ROOT_NUM (1 << STB__NPTR_ROOT_LOG2)
+#define STB__NPTR_ROOT_SHIFT (32 - STB__NPTR_ROOT_LOG2)
+
+#define STB__NPTR_NODE_LOG2 5
+#define STB__NPTR_NODE_NUM (1 << STB__NPTR_NODE_LOG2)
+#define STB__NPTR_NODE_MASK (STB__NPTR_NODE_NUM-1)
+#define STB__NPTR_NODE_SHIFT (STB__NPTR_ROOT_SHIFT - STB__NPTR_NODE_LOG2)
+#define STB__NPTR_NODE_OFFSET(x) (((x) >> STB__NPTR_NODE_SHIFT) & STB__NPTR_NODE_MASK)
+
+typedef struct stb__st_nptr
+{
+ void *ptr; // address of actual pointer
+ struct stb__st_nptr *next; // next pointer with same target
+ struct stb__st_nptr **prev; // prev pointer with same target, address of 'next' field (or first)
+ struct stb__st_nptr *next_in_block;
+} stb__nptr;
+
+typedef struct stb__st_nptr_target
+{
+ void *ptr; // address of target
+ stb__nptr *first; // address of first nptr pointing to this
+ struct stb__st_nptr_target *next_in_block;
+} stb__nptr_target;
+
+typedef struct
+{
+ stb__nptr *pointers;
+ stb__nptr_target *targets;
+} stb__memory_leaf;
+
+typedef struct
+{
+ stb__memory_leaf *children[STB__NPTR_NODE_NUM];
+} stb__memory_node;
+
+stb__memory_node *stb__memtab_root[STB__NPTR_ROOT_NUM];
+
+static stb__memory_leaf *stb__nptr_find_leaf(void *mem)
+{
+ stb_uint32 address = (stb_uint32) mem;
+ stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT];
+ if (z)
+ return z->children[STB__NPTR_NODE_OFFSET(address)];
+ else
+ return NULL;
+}
+
+static void * stb__nptr_alloc(int size)
+{
+ return stb__realloc_raw(0,size);
+}
+
+static void stb__nptr_free(void *p)
+{
+ stb__realloc_raw(p,0);
+}
+
+static stb__memory_leaf *stb__nptr_make_leaf(void *mem)
+{
+ stb_uint32 address = (stb_uint32) mem;
+ stb__memory_node *z = stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT];
+ stb__memory_leaf *f;
+ if (!z) {
+ int i;
+ z = (stb__memory_node *) stb__nptr_alloc(sizeof(*stb__memtab_root[0]));
+ stb__memtab_root[address >> STB__NPTR_ROOT_SHIFT] = z;
+ for (i=0; i < 256; ++i)
+ z->children[i] = 0;
+ }
+ f = (stb__memory_leaf *) stb__nptr_alloc(sizeof(*f));
+ z->children[STB__NPTR_NODE_OFFSET(address)] = f;
+ f->pointers = NULL;
+ f->targets = NULL;
+ return f;
+}
+
+static stb__nptr_target *stb__nptr_find_target(void *target, int force)
+{
+ stb__memory_leaf *p = stb__nptr_find_leaf(target);
+ if (p) {
+ stb__nptr_target *t = p->targets;
+ while (t) {
+ if (t->ptr == target)
+ return t;
+ t = t->next_in_block;
+ }
+ }
+ if (force) {
+ stb__nptr_target *t = (stb__nptr_target*) stb__nptr_alloc(sizeof(*t));
+ if (!p) p = stb__nptr_make_leaf(target);
+ t->ptr = target;
+ t->first = NULL;
+ t->next_in_block = p->targets;
+ p->targets = t;
+ return t;
+ } else
+ return NULL;
+}
+
+static stb__nptr *stb__nptr_find_pointer(void *ptr, int force)
+{
+ stb__memory_leaf *p = stb__nptr_find_leaf(ptr);
+ if (p) {
+ stb__nptr *t = p->pointers;
+ while (t) {
+ if (t->ptr == ptr)
+ return t;
+ t = t->next_in_block;
+ }
+ }
+ if (force) {
+ stb__nptr *t = (stb__nptr *) stb__nptr_alloc(sizeof(*t));
+ if (!p) p = stb__nptr_make_leaf(ptr);
+ t->ptr = ptr;
+ t->next = NULL;
+ t->prev = NULL;
+ t->next_in_block = p->pointers;
+ p->pointers = t;
+ return t;
+ } else
+ return NULL;
+}
+
+void stb_nptr_set(void *address_of_pointer, void *value_to_write)
+{
+ if (*(void **)address_of_pointer != value_to_write) {
+ *(void **) address_of_pointer = value_to_write;
+ stb_nptr_didset(address_of_pointer);
+ }
+}
+
+void stb_nptr_didset(void *address_of_pointer)
+{
+ // first unlink from old chain
+ void *new_address;
+ stb__nptr *p = stb__nptr_find_pointer(address_of_pointer, 1); // force building if doesn't exist
+ if (p->prev) { // if p->prev is NULL, we just built it, or it was NULL
+ *(p->prev) = p->next;
+ if (p->next) p->next->prev = p->prev;
+ }
+ // now add to new chain
+ new_address = *(void **)address_of_pointer;
+ if (new_address != NULL) {
+ stb__nptr_target *t = stb__nptr_find_target(new_address, 1);
+ p->next = t->first;
+ if (p->next) p->next->prev = &p->next;
+ p->prev = &t->first;
+ t->first = p;
+ } else {
+ p->prev = NULL;
+ p->next = NULL;
+ }
+}
+
+void stb__nptr_block(void *address, int len, void (*function)(stb__memory_leaf *f, int datum, void *start, void *end), int datum)
+{
+ void *end_address = (void *) ((char *) address + len - 1);
+ stb__memory_node *n;
+ stb_uint32 start = (stb_uint32) address;
+ stb_uint32 end = start + len - 1;
+
+ int b0 = start >> STB__NPTR_ROOT_SHIFT;
+ int b1 = end >> STB__NPTR_ROOT_SHIFT;
+ int b=b0,i,e0,e1;
+
+ e0 = STB__NPTR_NODE_OFFSET(start);
+
+ if (datum <= 0) {
+ // first block
+ n = stb__memtab_root[b0];
+ if (n) {
+ if (b0 != b1)
+ e1 = STB__NPTR_NODE_NUM-1;
+ else
+ e1 = STB__NPTR_NODE_OFFSET(end);
+ for (i=e0; i <= e1; ++i)
+ if (n->children[i])
+ function(n->children[i], datum, address, end_address);
+ }
+ if (b1 > b0) {
+ // blocks other than the first and last block
+ for (b=b0+1; b < b1; ++b) {
+ n = stb__memtab_root[b];
+ if (n)
+ for (i=0; i <= STB__NPTR_NODE_NUM-1; ++i)
+ if (n->children[i])
+ function(n->children[i], datum, address, end_address);
+ }
+ // last block
+ n = stb__memtab_root[b1];
+ if (n) {
+ e1 = STB__NPTR_NODE_OFFSET(end);
+ for (i=0; i <= e1; ++i)
+ if (n->children[i])
+ function(n->children[i], datum, address, end_address);
+ }
+ }
+ } else {
+ if (b1 > b0) {
+ // last block
+ n = stb__memtab_root[b1];
+ if (n) {
+ e1 = STB__NPTR_NODE_OFFSET(end);
+ for (i=e1; i >= 0; --i)
+ if (n->children[i])
+ function(n->children[i], datum, address, end_address);
+ }
+ // blocks other than the first and last block
+ for (b=b1-1; b > b0; --b) {
+ n = stb__memtab_root[b];
+ if (n)
+ for (i=STB__NPTR_NODE_NUM-1; i >= 0; --i)
+ if (n->children[i])
+ function(n->children[i], datum, address, end_address);
+ }
+ }
+ // first block
+ n = stb__memtab_root[b0];
+ if (n) {
+ if (b0 != b1)
+ e1 = STB__NPTR_NODE_NUM-1;
+ else
+ e1 = STB__NPTR_NODE_OFFSET(end);
+ for (i=e1; i >= e0; --i)
+ if (n->children[i])
+ function(n->children[i], datum, address, end_address);
+ }
+ }
+}
+
+static void stb__nptr_delete_pointers(stb__memory_leaf *f, int offset, void *start, void *end)
+{
+ stb__nptr **p = &f->pointers;
+ while (*p) {
+ stb__nptr *n = *p;
+ if (n->ptr >= start && n->ptr <= end) {
+ // unlink
+ if (n->prev) {
+ *(n->prev) = n->next;
+ if (n->next) n->next->prev = n->prev;
+ }
+ *p = n->next_in_block;
+ stb__nptr_free(n);
+ } else
+ p = &(n->next_in_block);
+ }
+}
+
+static void stb__nptr_delete_targets(stb__memory_leaf *f, int offset, void *start, void *end)
+{
+ stb__nptr_target **p = &f->targets;
+ while (*p) {
+ stb__nptr_target *n = *p;
+ if (n->ptr >= start && n->ptr <= end) {
+ // null pointers
+ stb__nptr *z = n->first;
+ while (z) {
+ stb__nptr *y = z->next;
+ z->prev = NULL;
+ z->next = NULL;
+ *(void **) z->ptr = NULL;
+ z = y;
+ }
+ // unlink this target
+ *p = n->next_in_block;
+ stb__nptr_free(n);
+ } else
+ p = &(n->next_in_block);
+ }
+}
+
+void stb_nptr_didfree(void *address_being_freed, int len)
+{
+ // step one: delete all pointers in this block
+ stb__nptr_block(address_being_freed, len, stb__nptr_delete_pointers, 0);
+ // step two: NULL all pointers to this block; do this second to avoid NULLing deleted pointers
+ stb__nptr_block(address_being_freed, len, stb__nptr_delete_targets, 0);
+}
+
+void stb_nptr_free(void *address_being_freed, int len)
+{
+ free(address_being_freed);
+ stb_nptr_didfree(address_being_freed, len);
+}
+
+static void stb__nptr_move_targets(stb__memory_leaf *f, int offset, void *start, void *end)
+{
+ stb__nptr_target **t = &f->targets;
+ while (*t) {
+ stb__nptr_target *n = *t;
+ if (n->ptr >= start && n->ptr <= end) {
+ stb__nptr *z;
+ stb__memory_leaf *f;
+ // unlink n
+ *t = n->next_in_block;
+ // update n to new address
+ n->ptr = (void *) ((char *) n->ptr + offset);
+ f = stb__nptr_find_leaf(n->ptr);
+ if (!f) f = stb__nptr_make_leaf(n->ptr);
+ n->next_in_block = f->targets;
+ f->targets = n;
+ // now go through all pointers and make them point here
+ z = n->first;
+ while (z) {
+ *(void**) z->ptr = n->ptr;
+ z = z->next;
+ }
+ } else
+ t = &(n->next_in_block);
+ }
+}
+
+static void stb__nptr_move_pointers(stb__memory_leaf *f, int offset, void *start, void *end)
+{
+ stb__nptr **p = &f->pointers;
+ while (*p) {
+ stb__nptr *n = *p;
+ if (n->ptr >= start && n->ptr <= end) {
+ // unlink
+ *p = n->next_in_block;
+ n->ptr = (void *) ((int) n->ptr + offset);
+ // move to new block
+ f = stb__nptr_find_leaf(n->ptr);
+ if (!f) f = stb__nptr_make_leaf(n->ptr);
+ n->next_in_block = f->pointers;
+ f->pointers = n;
+ } else
+ p = &(n->next_in_block);
+ }
+}
+
+void stb_nptr_realloc(void *new_address, void *old_address, int len)
+{
+ if (new_address == old_address) return;
+
+ // have to move the pointers first, because moving the targets
+ // requires writing to the pointers-to-the-targets, and if some of those moved too,
+ // we need to make sure we don't write to the old memory
+
+ // step one: move all pointers within the block
+ stb__nptr_block(old_address, len, stb__nptr_move_pointers, (char *) new_address - (char *) old_address);
+ // step two: move all targets within the block
+ stb__nptr_block(old_address, len, stb__nptr_move_targets, (char *) new_address - (char *) old_address);
+}
+
+void stb_nptr_move(void *new_address, void *old_address)
+{
+ stb_nptr_realloc(new_address, old_address, 1);
+}
+
+void stb_nptr_recache(void)
+{
+ int i,j;
+ for (i=0; i < STB__NPTR_ROOT_NUM; ++i)
+ if (stb__memtab_root[i])
+ for (j=0; j < STB__NPTR_NODE_NUM; ++j)
+ if (stb__memtab_root[i]->children[j]) {
+ stb__nptr *p = stb__memtab_root[i]->children[j]->pointers;
+ while (p) {
+ stb_nptr_didset(p->ptr);
+ p = p->next_in_block;
+ }
+ }
+}
+
+#endif // STB_DEFINE
+#endif // STB_NPTR
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// File Processing
+//
+
+
+#ifdef _WIN32
+ #define stb_rename(x,y) _wrename((const wchar_t *)stb__from_utf8(x), (const wchar_t *)stb__from_utf8_alt(y))
+#else
+ #define stb_rename rename
+#endif
+
+STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v);
+STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f);
+STB_EXTERN int stb_size_varlen64(stb_uint64 v);
+
+
+#define stb_filec (char *) stb_file
+#define stb_fileu (unsigned char *) stb_file
+STB_EXTERN void * stb_file(char *filename, size_t *length);
+STB_EXTERN void * stb_file_max(char *filename, size_t *length);
+STB_EXTERN size_t stb_filelen(FILE *f);
+STB_EXTERN int stb_filewrite(char *filename, void *data, size_t length);
+STB_EXTERN int stb_filewritestr(char *filename, char *data);
+STB_EXTERN char ** stb_stringfile(char *filename, int *len);
+STB_EXTERN char ** stb_stringfile_trimmed(char *name, int *len, char comm);
+STB_EXTERN char * stb_fgets(char *buffer, int buflen, FILE *f);
+STB_EXTERN char * stb_fgets_malloc(FILE *f);
+STB_EXTERN int stb_fexists(char *filename);
+STB_EXTERN int stb_fcmp(char *s1, char *s2);
+STB_EXTERN int stb_feq(char *s1, char *s2);
+STB_EXTERN time_t stb_ftimestamp(char *filename);
+
+STB_EXTERN int stb_fullpath(char *abs, int abs_size, char *rel);
+STB_EXTERN FILE * stb_fopen(char *filename, const char *mode);
+STB_EXTERN int stb_fclose(FILE *f, int keep);
+
+enum
+{
+ stb_keep_no = 0,
+ stb_keep_yes = 1,
+ stb_keep_if_different = 2,
+};
+
+STB_EXTERN int stb_copyfile(char *src, char *dest);
+
+STB_EXTERN void stb_fput_varlen64(FILE *f, stb_uint64 v);
+STB_EXTERN stb_uint64 stb_fget_varlen64(FILE *f);
+STB_EXTERN int stb_size_varlen64(stb_uint64 v);
+
+STB_EXTERN void stb_fwrite32(FILE *f, stb_uint32 datum);
+STB_EXTERN void stb_fput_varlen (FILE *f, int v);
+STB_EXTERN void stb_fput_varlenu(FILE *f, unsigned int v);
+STB_EXTERN int stb_fget_varlen (FILE *f);
+STB_EXTERN stb_uint stb_fget_varlenu(FILE *f);
+STB_EXTERN void stb_fput_ranged (FILE *f, int v, int b, stb_uint n);
+STB_EXTERN int stb_fget_ranged (FILE *f, int b, stb_uint n);
+STB_EXTERN int stb_size_varlen (int v);
+STB_EXTERN int stb_size_varlenu(unsigned int v);
+STB_EXTERN int stb_size_ranged (int b, stb_uint n);
+
+STB_EXTERN int stb_fread(void *data, size_t len, size_t count, void *f);
+STB_EXTERN int stb_fwrite(void *data, size_t len, size_t count, void *f);
+
+#if 0
+typedef struct
+{
+ FILE *base_file;
+ char *buffer;
+ int buffer_size;
+ int buffer_off;
+ int buffer_left;
+} STBF;
+
+STB_EXTERN STBF *stb_tfopen(char *filename, char *mode);
+STB_EXTERN int stb_tfread(void *data, size_t len, size_t count, STBF *f);
+STB_EXTERN int stb_tfwrite(void *data, size_t len, size_t count, STBF *f);
+#endif
+
+#ifdef STB_DEFINE
+
+#if 0
+STBF *stb_tfopen(char *filename, char *mode)
+{
+ STBF *z;
+ FILE *f = stb_p_fopen(filename, mode);
+ if (!f) return NULL;
+ z = (STBF *) malloc(sizeof(*z));
+ if (!z) { fclose(f); return NULL; }
+ z->base_file = f;
+ if (!strcmp(mode, "rb") || !strcmp(mode, "wb")) {
+ z->buffer_size = 4096;
+ z->buffer_off = z->buffer_size;
+ z->buffer_left = 0;
+ z->buffer = malloc(z->buffer_size);
+ if (!z->buffer) { free(z); fclose(f); return NULL; }
+ } else {
+ z->buffer = 0;
+ z->buffer_size = 0;
+ z->buffer_left = 0;
+ }
+ return z;
+}
+
+int stb_tfread(void *data, size_t len, size_t count, STBF *f)
+{
+ int total = len*count, done=0;
+ if (!total) return 0;
+ if (total <= z->buffer_left) {
+ memcpy(data, z->buffer + z->buffer_off, total);
+ z->buffer_off += total;
+ z->buffer_left -= total;
+ return count;
+ } else {
+ char *out = (char *) data;
+
+ // consume all buffered data
+ memcpy(data, z->buffer + z->buffer_off, z->buffer_left);
+ done = z->buffer_left;
+ out += z->buffer_left;
+ z->buffer_left=0;
+
+ if (total-done > (z->buffer_size >> 1)) {
+ done += fread(out
+ }
+ }
+}
+#endif
+
+void stb_fwrite32(FILE *f, stb_uint32 x)
+{
+ fwrite(&x, 4, 1, f);
+}
+
+#if defined(_WIN32)
+ #define stb__stat _stat
+#else
+ #define stb__stat stat
+#endif
+
+int stb_fexists(char *filename)
+{
+ struct stb__stat buf;
+ return stb__windows(
+ _wstat((const wchar_t *)stb__from_utf8(filename), &buf),
+ stat(filename,&buf)
+ ) == 0;
+}
+
+time_t stb_ftimestamp(char *filename)
+{
+ struct stb__stat buf;
+ if (stb__windows(
+ _wstat((const wchar_t *)stb__from_utf8(filename), &buf),
+ stat(filename,&buf)
+ ) == 0)
+ {
+ return buf.st_mtime;
+ } else {
+ return 0;
+ }
+}
+
+size_t stb_filelen(FILE *f)
+{
+ long len, pos;
+ pos = ftell(f);
+ fseek(f, 0, SEEK_END);
+ len = ftell(f);
+ fseek(f, pos, SEEK_SET);
+ return (size_t) len;
+}
+
+void *stb_file(char *filename, size_t *length)
+{
+ FILE *f = stb__fopen(filename, "rb");
+ char *buffer;
+ size_t len, len2;
+ if (!f) return NULL;
+ len = stb_filelen(f);
+ buffer = (char *) malloc(len+2); // nul + extra
+ len2 = fread(buffer, 1, len, f);
+ if (len2 == len) {
+ if (length) *length = len;
+ buffer[len] = 0;
+ } else {
+ free(buffer);
+ buffer = NULL;
+ }
+ fclose(f);
+ return buffer;
+}
+
+int stb_filewrite(char *filename, void *data, size_t length)
+{
+ FILE *f = stb_fopen(filename, "wb");
+ if (f) {
+ unsigned char *data_ptr = (unsigned char *) data;
+ size_t remaining = length;
+ while (remaining > 0) {
+ size_t len2 = remaining > 65536 ? 65536 : remaining;
+ size_t len3 = fwrite(data_ptr, 1, len2, f);
+ if (len2 != len3) {
+ fprintf(stderr, "Failed while writing %s\n", filename);
+ break;
+ }
+ remaining -= len2;
+ data_ptr += len2;
+ }
+ stb_fclose(f, stb_keep_if_different);
+ }
+ return f != NULL;
+}
+
+int stb_filewritestr(char *filename, char *data)
+{
+ return stb_filewrite(filename, data, strlen(data));
+}
+
+void * stb_file_max(char *filename, size_t *length)
+{
+ FILE *f = stb__fopen(filename, "rb");
+ char *buffer;
+ size_t len, maxlen;
+ if (!f) return NULL;
+ maxlen = *length;
+ buffer = (char *) malloc(maxlen+1);
+ len = fread(buffer, 1, maxlen, f);
+ buffer[len] = 0;
+ fclose(f);
+ *length = len;
+ return buffer;
+}
+
+char ** stb_stringfile(char *filename, int *plen)
+{
+ FILE *f = stb__fopen(filename, "rb");
+ char *buffer, **list=NULL, *s;
+ size_t len, count, i;
+
+ if (!f) return NULL;
+ len = stb_filelen(f);
+ buffer = (char *) malloc(len+1);
+ len = fread(buffer, 1, len, f);
+ buffer[len] = 0;
+ fclose(f);
+
+ // two passes through: first time count lines, second time set them
+ for (i=0; i < 2; ++i) {
+ s = buffer;
+ if (i == 1)
+ list[0] = s;
+ count = 1;
+ while (*s) {
+ if (*s == '\n' || *s == '\r') {
+ // detect if both cr & lf are together
+ int crlf = (s[0] + s[1]) == ('\n' + '\r');
+ if (i == 1) *s = 0;
+ if (crlf) ++s;
+ if (s[1]) { // it's not over yet
+ if (i == 1) list[count] = s+1;
+ ++count;
+ }
+ }
+ ++s;
+ }
+ if (i == 0) {
+ list = (char **) malloc(sizeof(*list) * (count+1) + len+1);
+ if (!list) return NULL;
+ list[count] = 0;
+ // recopy the file so there's just a single allocation to free
+ memcpy(&list[count+1], buffer, len+1);
+ free(buffer);
+ buffer = (char *) &list[count+1];
+ if (plen) *plen = (int) count;
+ }
+ }
+ return list;
+}
+
+char ** stb_stringfile_trimmed(char *name, int *len, char comment)
+{
+ int i,n,o=0;
+ char **s = stb_stringfile(name, &n);
+ if (s == NULL) return NULL;
+ for (i=0; i < n; ++i) {
+ char *p = stb_skipwhite(s[i]);
+ if (*p && *p != comment)
+ s[o++] = p;
+ }
+ s[o] = NULL;
+ if (len) *len = o;
+ return s;
+}
+
+char * stb_fgets(char *buffer, int buflen, FILE *f)
+{
+ char *p;
+ buffer[0] = 0;
+ p = fgets(buffer, buflen, f);
+ if (p) {
+ int n = (int) (strlen(p)-1);
+ if (n >= 0)
+ if (p[n] == '\n')
+ p[n] = 0;
+ }
+ return p;
+}
+
+char * stb_fgets_malloc(FILE *f)
+{
+ // avoid reallocing for small strings
+ char quick_buffer[800];
+ quick_buffer[sizeof(quick_buffer)-2] = 0;
+ if (!fgets(quick_buffer, sizeof(quick_buffer), f))
+ return NULL;
+
+ if (quick_buffer[sizeof(quick_buffer)-2] == 0) {
+ size_t n = strlen(quick_buffer);
+ if (n > 0 && quick_buffer[n-1] == '\n')
+ quick_buffer[n-1] = 0;
+ return stb_p_strdup(quick_buffer);
+ } else {
+ char *p;
+ char *a = stb_p_strdup(quick_buffer);
+ size_t len = sizeof(quick_buffer)-1;
+
+ while (!feof(f)) {
+ if (a[len-1] == '\n') break;
+ a = (char *) realloc(a, len*2);
+ p = &a[len];
+ p[len-2] = 0;
+ if (!fgets(p, (int) len, f))
+ break;
+ if (p[len-2] == 0) {
+ len += strlen(p);
+ break;
+ }
+ len = len + (len-1);
+ }
+ if (a[len-1] == '\n')
+ a[len-1] = 0;
+ return a;
+ }
+}
+
+int stb_fullpath(char *abs, int abs_size, char *rel)
+{
+ #ifdef _WIN32
+ return _fullpath(abs, rel, abs_size) != NULL;
+ #else
+ if (rel[0] == '/' || rel[0] == '~') {
+ if ((int) strlen(rel) >= abs_size)
+ return 0;
+ stb_p_strcpy_s(abs,65536,rel);
+ return STB_TRUE;
+ } else {
+ int n;
+ getcwd(abs, abs_size);
+ n = strlen(abs);
+ if (n+(int) strlen(rel)+2 <= abs_size) {
+ abs[n] = '/';
+ stb_p_strcpy_s(abs+n+1, 65536,rel);
+ return STB_TRUE;
+ } else {
+ return STB_FALSE;
+ }
+ }
+ #endif
+}
+
+static int stb_fcmp_core(FILE *f, FILE *g)
+{
+ char buf1[1024],buf2[1024];
+ int n1,n2, res=0;
+
+ while (1) {
+ n1 = (int) fread(buf1, 1, sizeof(buf1), f);
+ n2 = (int) fread(buf2, 1, sizeof(buf2), g);
+ res = memcmp(buf1,buf2,stb_min(n1,n2));
+ if (res)
+ break;
+ if (n1 != n2) {
+ res = n1 < n2 ? -1 : 1;
+ break;
+ }
+ if (n1 == 0)
+ break;
+ }
+
+ fclose(f);
+ fclose(g);
+ return res;
+}
+
+int stb_fcmp(char *s1, char *s2)
+{
+ FILE *f = stb__fopen(s1, "rb");
+ FILE *g = stb__fopen(s2, "rb");
+
+ if (f == NULL || g == NULL) {
+ if (f) fclose(f);
+ if (g) {
+ fclose(g);
+ return STB_TRUE;
+ }
+ return f != NULL;
+ }
+
+ return stb_fcmp_core(f,g);
+}
+
+int stb_feq(char *s1, char *s2)
+{
+ FILE *f = stb__fopen(s1, "rb");
+ FILE *g = stb__fopen(s2, "rb");
+
+ if (f == NULL || g == NULL) {
+ if (f) fclose(f);
+ if (g) fclose(g);
+ return f == g;
+ }
+
+ // feq is faster because it shortcuts if they're different length
+ if (stb_filelen(f) != stb_filelen(g)) {
+ fclose(f);
+ fclose(g);
+ return 0;
+ }
+
+ return !stb_fcmp_core(f,g);
+}
+
+static stb_ptrmap *stb__files;
+
+typedef struct
+{
+ char *temp_name;
+ char *name;
+ int errors;
+} stb__file_data;
+
+static FILE *stb__open_temp_file(char *temp_name, char *src_name, const char *mode)
+{
+ size_t p;
+#ifdef _MSC_VER
+ int j;
+#endif
+ FILE *f;
+ // try to generate a temporary file in the same directory
+ p = strlen(src_name)-1;
+ while (p > 0 && src_name[p] != '/' && src_name[p] != '\\'
+ && src_name[p] != ':' && src_name[p] != '~')
+ --p;
+ ++p;
+
+ memcpy(temp_name, src_name, p);
+
+ #ifdef _MSC_VER
+ // try multiple times to make a temp file... just in
+ // case some other process makes the name first
+ for (j=0; j < 32; ++j) {
+ stb_p_strcpy_s(temp_name+p, 65536, "stmpXXXXXX");
+ if (!stb_p_mktemp(temp_name))
+ return 0;
+
+ f = stb_p_fopen(temp_name, mode);
+ if (f != NULL)
+ break;
+ }
+ #else
+ {
+ stb_p_strcpy_s(temp_name+p, 65536, "stmpXXXXXX");
+ #ifdef __MINGW32__
+ int fd = open(stb_p_mktemp(temp_name), O_RDWR);
+ #else
+ int fd = mkstemp(temp_name);
+ #endif
+ if (fd == -1) return NULL;
+ f = fdopen(fd, mode);
+ if (f == NULL) {
+ unlink(temp_name);
+ close(fd);
+ return NULL;
+ }
+ }
+ #endif
+ return f;
+}
+
+
+FILE * stb_fopen(char *filename, const char *mode)
+{
+ FILE *f;
+ char name_full[4096];
+ char temp_full[sizeof(name_full) + 12];
+
+ // @TODO: if the file doesn't exist, we can also use the fastpath here
+ if (mode[0] != 'w' && !strchr(mode, '+'))
+ return stb__fopen(filename, mode);
+
+ // save away the full path to the file so if the program
+ // changes the cwd everything still works right! unix has
+ // better ways to do this, but we have to work in windows
+ name_full[0] = '\0'; // stb_fullpath reads name_full[0]
+ if (stb_fullpath(name_full, sizeof(name_full), filename)==0)
+ return 0;
+
+ f = stb__open_temp_file(temp_full, name_full, mode);
+ if (f != NULL) {
+ stb__file_data *d = (stb__file_data *) malloc(sizeof(*d));
+ if (!d) { assert(0); /* NOTREACHED */fclose(f); return NULL; }
+ if (stb__files == NULL) stb__files = stb_ptrmap_create();
+ d->temp_name = stb_p_strdup(temp_full);
+ d->name = stb_p_strdup(name_full);
+ d->errors = 0;
+ stb_ptrmap_add(stb__files, f, d);
+ return f;
+ }
+
+ return NULL;
+}
+
+int stb_fclose(FILE *f, int keep)
+{
+ stb__file_data *d;
+
+ int ok = STB_FALSE;
+ if (f == NULL) return 0;
+
+ if (ferror(f))
+ keep = stb_keep_no;
+
+ fclose(f);
+
+ if (stb__files && stb_ptrmap_remove(stb__files, f, (void **) &d)) {
+ if (stb__files->count == 0) {
+ stb_ptrmap_destroy(stb__files);
+ stb__files = NULL;
+ }
+ } else
+ return STB_TRUE; // not special
+
+ if (keep == stb_keep_if_different) {
+ // check if the files are identical
+ if (stb_feq(d->name, d->temp_name)) {
+ keep = stb_keep_no;
+ ok = STB_TRUE; // report success if no change
+ }
+ }
+
+ if (keep == stb_keep_no) {
+ remove(d->temp_name);
+ } else {
+ if (!stb_fexists(d->name)) {
+ // old file doesn't exist, so just move the new file over it
+ stb_rename(d->temp_name, d->name);
+ } else {
+ // don't delete the old file yet in case there are troubles! First rename it!
+ char preserved_old_file[4096];
+
+ // generate a temp filename in the same directory (also creates it, which we don't need)
+ FILE *dummy = stb__open_temp_file(preserved_old_file, d->name, "wb");
+ if (dummy != NULL) {
+ // we don't actually want the open file
+ fclose(dummy);
+
+ // discard what we just created
+ remove(preserved_old_file); // if this fails, there's nothing we can do, and following logic handles it as best as possible anyway
+
+ // move the existing file to the preserved name
+ if (0 != stb_rename(d->name, preserved_old_file)) { // 0 on success
+ // failed, state is:
+ // filename -> old file
+ // tempname -> new file
+ // keep tempname around so we don't lose data
+ } else {
+ // state is:
+ // preserved -> old file
+ // tempname -> new file
+ // move the new file to the old name
+ if (0 == stb_rename(d->temp_name, d->name)) {
+ // state is:
+ // preserved -> old file
+ // filename -> new file
+ ok = STB_TRUE;
+
+ // 'filename -> new file' has always been the goal, so clean up
+ remove(preserved_old_file); // nothing to be done if it fails
+ } else {
+ // couldn't rename, so try renaming preserved file back
+
+ // state is:
+ // preserved -> old file
+ // tempname -> new file
+ stb_rename(preserved_old_file, d->name);
+ // if the rename failed, there's nothing more we can do
+ }
+ }
+ } else {
+ // we couldn't get a temp filename. do this the naive way; the worst case failure here
+ // leaves the filename pointing to nothing and the new file as a tempfile
+ remove(d->name);
+ stb_rename(d->temp_name, d->name);
+ }
+ }
+ }
+
+ free(d->temp_name);
+ free(d->name);
+ free(d);
+
+ return ok;
+}
+
+int stb_copyfile(char *src, char *dest)
+{
+ char raw_buffer[1024];
+ char *buffer;
+ int buf_size = 65536;
+
+ FILE *f, *g;
+
+ // if file already exists at destination, do nothing
+ if (stb_feq(src, dest)) return STB_TRUE;
+
+ // open file
+ f = stb__fopen(src, "rb");
+ if (f == NULL) return STB_FALSE;
+
+ // open file for writing
+ g = stb__fopen(dest, "wb");
+ if (g == NULL) {
+ fclose(f);
+ return STB_FALSE;
+ }
+
+ buffer = (char *) malloc(buf_size);
+ if (buffer == NULL) {
+ buffer = raw_buffer;
+ buf_size = sizeof(raw_buffer);
+ }
+
+ while (!feof(f)) {
+ size_t n = fread(buffer, 1, buf_size, f);
+ if (n != 0)
+ fwrite(buffer, 1, n, g);
+ }
+
+ fclose(f);
+ if (buffer != raw_buffer)
+ free(buffer);
+
+ fclose(g);
+ return STB_TRUE;
+}
+
+// varlen:
+// v' = (v >> 31) + (v < 0 ? ~v : v)<<1; // small abs(v) => small v'
+// output v as big endian v'+k for v' <= k:
+// 1 byte : v' <= 0x00000080 ( -64 <= v < 64) 7 bits
+// 2 bytes: v' <= 0x00004000 (-8192 <= v < 8192) 14 bits
+// 3 bytes: v' <= 0x00200000 21 bits
+// 4 bytes: v' <= 0x10000000 28 bits
+// the number of most significant 1-bits in the first byte
+// equals the number of bytes after the first
+
+#define stb__varlen_xform(v) (v<0 ? (~v << 1)+1 : (v << 1))
+
+int stb_size_varlen(int v) { return stb_size_varlenu(stb__varlen_xform(v)); }
+int stb_size_varlenu(unsigned int v)
+{
+ if (v < 0x00000080) return 1;
+ if (v < 0x00004000) return 2;
+ if (v < 0x00200000) return 3;
+ if (v < 0x10000000) return 4;
+ return 5;
+}
+
+void stb_fput_varlen(FILE *f, int v) { stb_fput_varlenu(f, stb__varlen_xform(v)); }
+
+void stb_fput_varlenu(FILE *f, unsigned int z)
+{
+ if (z >= 0x10000000) fputc(0xF0,f);
+ if (z >= 0x00200000) fputc((z < 0x10000000 ? 0xE0 : 0)+(z>>24),f);
+ if (z >= 0x00004000) fputc((z < 0x00200000 ? 0xC0 : 0)+(z>>16),f);
+ if (z >= 0x00000080) fputc((z < 0x00004000 ? 0x80 : 0)+(z>> 8),f);
+ fputc(z,f);
+}
+
+#define stb_fgetc(f) ((unsigned char) fgetc(f))
+
+int stb_fget_varlen(FILE *f)
+{
+ unsigned int z = stb_fget_varlenu(f);
+ return (z & 1) ? ~(z>>1) : (z>>1);
+}
+
+unsigned int stb_fget_varlenu(FILE *f)
+{
+ unsigned int z;
+ unsigned char d;
+ d = stb_fgetc(f);
+
+ if (d >= 0x80) {
+ if (d >= 0xc0) {
+ if (d >= 0xe0) {
+ if (d == 0xf0) z = stb_fgetc(f) << 24;
+ else z = (d - 0xe0) << 24;
+ z += stb_fgetc(f) << 16;
+ }
+ else
+ z = (d - 0xc0) << 16;
+ z += stb_fgetc(f) << 8;
+ } else
+ z = (d - 0x80) << 8;
+ z += stb_fgetc(f);
+ } else
+ z = d;
+ return z;
+}
+
+stb_uint64 stb_fget_varlen64(FILE *f)
+{
+ stb_uint64 z;
+ unsigned char d;
+ d = stb_fgetc(f);
+
+ if (d >= 0x80) {
+ if (d >= 0xc0) {
+ if (d >= 0xe0) {
+ if (d >= 0xf0) {
+ if (d >= 0xf8) {
+ if (d >= 0xfc) {
+ if (d >= 0xfe) {
+ if (d >= 0xff)
+ z = (stb_uint64) stb_fgetc(f) << 56;
+ else
+ z = (stb_uint64) (d - 0xfe) << 56;
+ z |= (stb_uint64) stb_fgetc(f) << 48;
+ } else z = (stb_uint64) (d - 0xfc) << 48;
+ z |= (stb_uint64) stb_fgetc(f) << 40;
+ } else z = (stb_uint64) (d - 0xf8) << 40;
+ z |= (stb_uint64) stb_fgetc(f) << 32;
+ } else z = (stb_uint64) (d - 0xf0) << 32;
+ z |= (stb_uint) stb_fgetc(f) << 24;
+ } else z = (stb_uint) (d - 0xe0) << 24;
+ z |= (stb_uint) stb_fgetc(f) << 16;
+ } else z = (stb_uint) (d - 0xc0) << 16;
+ z |= (stb_uint) stb_fgetc(f) << 8;
+ } else z = (stb_uint) (d - 0x80) << 8;
+ z |= stb_fgetc(f);
+ } else
+ z = d;
+
+ return (z & 1) ? ~(z >> 1) : (z >> 1);
+}
+
+int stb_size_varlen64(stb_uint64 v)
+{
+ if (v < 0x00000080) return 1;
+ if (v < 0x00004000) return 2;
+ if (v < 0x00200000) return 3;
+ if (v < 0x10000000) return 4;
+ if (v < STB_IMM_UINT64(0x0000000800000000)) return 5;
+ if (v < STB_IMM_UINT64(0x0000040000000000)) return 6;
+ if (v < STB_IMM_UINT64(0x0002000000000000)) return 7;
+ if (v < STB_IMM_UINT64(0x0100000000000000)) return 8;
+ return 9;
+}
+
+void stb_fput_varlen64(FILE *f, stb_uint64 v)
+{
+ stb_uint64 z = stb__varlen_xform(v);
+ int first=1;
+ if (z >= STB_IMM_UINT64(0x100000000000000)) {
+ fputc(0xff,f);
+ first=0;
+ }
+ if (z >= STB_IMM_UINT64(0x02000000000000)) fputc((first ? 0xFE : 0)+(char)(z>>56),f), first=0;
+ if (z >= STB_IMM_UINT64(0x00040000000000)) fputc((first ? 0xFC : 0)+(char)(z>>48),f), first=0;
+ if (z >= STB_IMM_UINT64(0x00000800000000)) fputc((first ? 0xF8 : 0)+(char)(z>>40),f), first=0;
+ if (z >= STB_IMM_UINT64(0x00000010000000)) fputc((first ? 0xF0 : 0)+(char)(z>>32),f), first=0;
+ if (z >= STB_IMM_UINT64(0x00000000200000)) fputc((first ? 0xE0 : 0)+(char)(z>>24),f), first=0;
+ if (z >= STB_IMM_UINT64(0x00000000004000)) fputc((first ? 0xC0 : 0)+(char)(z>>16),f), first=0;
+ if (z >= STB_IMM_UINT64(0x00000000000080)) fputc((first ? 0x80 : 0)+(char)(z>> 8),f), first=0;
+ fputc((char)z,f);
+}
+
+void stb_fput_ranged(FILE *f, int v, int b, stb_uint n)
+{
+ v -= b;
+ if (n <= (1 << 31))
+ assert((stb_uint) v < n);
+ if (n > (1 << 24)) fputc(v >> 24, f);
+ if (n > (1 << 16)) fputc(v >> 16, f);
+ if (n > (1 << 8)) fputc(v >> 8, f);
+ fputc(v,f);
+}
+
+int stb_fget_ranged(FILE *f, int b, stb_uint n)
+{
+ unsigned int v=0;
+ if (n > (1 << 24)) v += stb_fgetc(f) << 24;
+ if (n > (1 << 16)) v += stb_fgetc(f) << 16;
+ if (n > (1 << 8)) v += stb_fgetc(f) << 8;
+ v += stb_fgetc(f);
+ return b+v;
+}
+
+int stb_size_ranged(int b, stb_uint n)
+{
+ if (n > (1 << 24)) return 4;
+ if (n > (1 << 16)) return 3;
+ if (n > (1 << 8)) return 2;
+ return 1;
+}
+
+void stb_fput_string(FILE *f, char *s)
+{
+ size_t len = strlen(s);
+ stb_fput_varlenu(f, (unsigned int) len);
+ fwrite(s, 1, len, f);
+}
+
+// inverse of the above algorithm
+char *stb_fget_string(FILE *f, void *p)
+{
+ char *s;
+ int len = stb_fget_varlenu(f);
+ if (len > 4096) return NULL;
+ s = p ? stb_malloc_string(p, len+1) : (char *) malloc(len+1);
+ fread(s, 1, len, f);
+ s[len] = 0;
+ return s;
+}
+
+char *stb_strdup(char *str, void *pool)
+{
+ size_t len = strlen(str);
+ char *p = stb_malloc_string(pool, len+1);
+ stb_p_strcpy_s(p, len+1, str);
+ return p;
+}
+
+// strip the trailing '/' or '\\' from a directory so we can refer to it
+// as a file for _stat()
+char *stb_strip_final_slash(char *t)
+{
+ if (t[0]) {
+ char *z = t + strlen(t) - 1;
+ // *z is the last character
+ if (*z == '\\' || *z == '/')
+ if (z != t+2 || t[1] != ':') // but don't strip it if it's e.g. "c:/"
+ *z = 0;
+ if (*z == '\\')
+ *z = '/'; // canonicalize to make sure it matches db
+ }
+ return t;
+}
+
+char *stb_strip_final_slash_regardless(char *t)
+{
+ if (t[0]) {
+ char *z = t + strlen(t) - 1;
+ // *z is the last character
+ if (*z == '\\' || *z == '/')
+ *z = 0;
+ if (*z == '\\')
+ *z = '/'; // canonicalize to make sure it matches db
+ }
+ return t;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Options parsing
+//
+
+STB_EXTERN char **stb_getopt_param(int *argc, char **argv, char *param);
+STB_EXTERN char **stb_getopt(int *argc, char **argv);
+STB_EXTERN void stb_getopt_free(char **opts);
+
+#ifdef STB_DEFINE
+
+void stb_getopt_free(char **opts)
+{
+ int i;
+ char ** o2 = opts;
+ for (i=0; i < stb_arr_len(o2); ++i)
+ free(o2[i]);
+ stb_arr_free(o2);
+}
+
+char **stb_getopt(int *argc, char **argv)
+{
+ return stb_getopt_param(argc, argv, (char*) "");
+}
+
+char **stb_getopt_param(int *argc, char **argv, char *param)
+{
+ char ** opts=NULL;
+ int i,j=1;
+ for (i=1; i < *argc; ++i) {
+ if (argv[i][0] != '-') {
+ argv[j++] = argv[i];
+ } else {
+ if (argv[i][1] == 0) { // plain - == don't parse further options
+ ++i;
+ while (i < *argc)
+ argv[j++] = argv[i++];
+ break;
+ } else if (argv[i][1] == '-') {
+ // copy argument through including initial '-' for clarity
+ stb_arr_push(opts, stb_p_strdup(argv[i]));
+ } else {
+ int k;
+ char *q = argv[i]; // traverse options list
+ for (k=1; q[k]; ++k) {
+ char *s;
+ if (strchr(param, q[k])) { // does it take a parameter?
+ char *t = &q[k+1], z = q[k];
+ size_t len=0;
+ if (*t == 0) {
+ if (i == *argc-1) { // takes a parameter, but none found
+ *argc = 0;
+ stb_getopt_free(opts);
+ return NULL;
+ }
+ t = argv[++i];
+ } else
+ k += (int) strlen(t);
+ len = strlen(t);
+ s = (char *) malloc(len+2);
+ if (!s) return NULL;
+ s[0] = z;
+ stb_p_strcpy_s(s+1, len+2, t);
+ } else {
+ // no parameter
+ s = (char *) malloc(2);
+ if (!s) return NULL;
+ s[0] = q[k];
+ s[1] = 0;
+ }
+ stb_arr_push(opts, s);
+ }
+ }
+ }
+ }
+ stb_arr_push(opts, NULL);
+ *argc = j;
+ return opts;
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Portable directory reading
+//
+
+STB_EXTERN char **stb_readdir_files (char *dir);
+STB_EXTERN char **stb_readdir_files_mask(char *dir, char *wild);
+STB_EXTERN char **stb_readdir_subdirs(char *dir);
+STB_EXTERN char **stb_readdir_subdirs_mask(char *dir, char *wild);
+STB_EXTERN void stb_readdir_free (char **files);
+STB_EXTERN char **stb_readdir_recursive(char *dir, char *filespec);
+STB_EXTERN void stb_delete_directory_recursive(char *dir);
+
+#ifdef STB_DEFINE
+
+#ifdef _MSC_VER
+#include <io.h>
+#else
+#include <unistd.h>
+#include <dirent.h>
+#endif
+
+void stb_readdir_free(char **files)
+{
+ char **f2 = files;
+ int i;
+ for (i=0; i < stb_arr_len(f2); ++i)
+ free(f2[i]);
+ stb_arr_free(f2);
+}
+
+static int isdotdirname(char *name)
+{
+ if (name[0] == '.')
+ return (name[1] == '.') ? !name[2] : !name[1];
+ return 0;
+}
+
+STB_EXTERN int stb_wildmatchi(char *expr, char *candidate);
+static char **readdir_raw(char *dir, int return_subdirs, char *mask)
+{
+ char **results = NULL;
+ char buffer[4096], with_slash[4096];
+ size_t n;
+
+ #ifdef _MSC_VER
+ stb__wchar *ws;
+ struct _wfinddata_t data;
+ #ifdef _WIN64
+ const intptr_t none = -1;
+ intptr_t z;
+ #else
+ const long none = -1;
+ long z;
+ #endif
+ #else // !_MSC_VER
+ const DIR *none = NULL;
+ DIR *z;
+ #endif
+
+ n = stb_strscpy(buffer,dir,sizeof(buffer));
+ if (!n || n >= sizeof(buffer))
+ return NULL;
+ stb_fixpath(buffer);
+ n--;
+
+ if (n > 0 && (buffer[n-1] != '/')) {
+ buffer[n++] = '/';
+ }
+ buffer[n] = 0;
+ if (!stb_strscpy(with_slash,buffer,sizeof(with_slash)))
+ return NULL;
+
+ #ifdef _MSC_VER
+ if (!stb_strscpy(buffer+n,"*.*",sizeof(buffer)-n))
+ return NULL;
+ ws = stb__from_utf8(buffer);
+ z = _wfindfirst((const wchar_t *)ws, &data);
+ #else
+ z = opendir(dir);
+ #endif
+
+ if (z != none) {
+ int nonempty = STB_TRUE;
+ #ifndef _MSC_VER
+ struct dirent *data = readdir(z);
+ nonempty = (data != NULL);
+ #endif
+
+ if (nonempty) {
+
+ do {
+ int is_subdir;
+ #ifdef _MSC_VER
+ char *name = stb__to_utf8((stb__wchar *)data.name);
+ if (name == NULL) {
+ fprintf(stderr, "%s to convert '%S' to %s!\n", "Unable", data.name, "utf8");
+ continue;
+ }
+ is_subdir = !!(data.attrib & _A_SUBDIR);
+ #else
+ char *name = data->d_name;
+ if (!stb_strscpy(buffer+n,name,sizeof(buffer)-n))
+ break;
+ // Could follow DT_LNK, but would need to check for recursive links.
+ is_subdir = !!(data->d_type & DT_DIR);
+ #endif
+
+ if (is_subdir == return_subdirs) {
+ if (!is_subdir || !isdotdirname(name)) {
+ if (!mask || stb_wildmatchi(mask, name)) {
+ char buffer[4096],*p=buffer;
+ if ( stb_snprintf(buffer, sizeof(buffer), "%s%s", with_slash, name) < 0 )
+ break;
+ if (buffer[0] == '.' && buffer[1] == '/')
+ p = buffer+2;
+ stb_arr_push(results, stb_p_strdup(p));
+ }
+ }
+ }
+ }
+ #ifdef _MSC_VER
+ while (0 == _wfindnext(z, &data));
+ #else
+ while ((data = readdir(z)) != NULL);
+ #endif
+ }
+ #ifdef _MSC_VER
+ _findclose(z);
+ #else
+ closedir(z);
+ #endif
+ }
+ return results;
+}
+
+char **stb_readdir_files (char *dir) { return readdir_raw(dir, 0, NULL); }
+char **stb_readdir_subdirs(char *dir) { return readdir_raw(dir, 1, NULL); }
+char **stb_readdir_files_mask(char *dir, char *wild) { return readdir_raw(dir, 0, wild); }
+char **stb_readdir_subdirs_mask(char *dir, char *wild) { return readdir_raw(dir, 1, wild); }
+
+int stb__rec_max=0x7fffffff;
+static char **stb_readdir_rec(char **sofar, char *dir, char *filespec)
+{
+ char **files;
+ char ** dirs;
+ char **p;
+
+ if (stb_arr_len(sofar) >= stb__rec_max) return sofar;
+
+ files = stb_readdir_files_mask(dir, filespec);
+ stb_arr_for(p, files) {
+ stb_arr_push(sofar, stb_p_strdup(*p));
+ if (stb_arr_len(sofar) >= stb__rec_max) break;
+ }
+ stb_readdir_free(files);
+ if (stb_arr_len(sofar) >= stb__rec_max) return sofar;
+
+ dirs = stb_readdir_subdirs(dir);
+ stb_arr_for(p, dirs)
+ sofar = stb_readdir_rec(sofar, *p, filespec);
+ stb_readdir_free(dirs);
+ return sofar;
+}
+
+char **stb_readdir_recursive(char *dir, char *filespec)
+{
+ return stb_readdir_rec(NULL, dir, filespec);
+}
+
+void stb_delete_directory_recursive(char *dir)
+{
+ char **list = stb_readdir_subdirs(dir);
+ int i;
+ for (i=0; i < stb_arr_len(list); ++i)
+ stb_delete_directory_recursive(list[i]);
+ stb_arr_free(list);
+ list = stb_readdir_files(dir);
+ for (i=0; i < stb_arr_len(list); ++i)
+ if (!remove(list[i])) {
+ // on windows, try again after making it writeable; don't ALWAYS
+ // do this first since that would be slow in the normal case
+ #ifdef _MSC_VER
+ _chmod(list[i], _S_IWRITE);
+ remove(list[i]);
+ #endif
+ }
+ stb_arr_free(list);
+ stb__windows(_rmdir,rmdir)(dir);
+}
+
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// construct trees from filenames; useful for cmirror summaries
+
+typedef struct stb_dirtree2 stb_dirtree2;
+
+struct stb_dirtree2
+{
+ stb_dirtree2 **subdirs;
+
+ // make convenient for stb_summarize_tree
+ int num_subdir;
+ float weight;
+
+ // actual data
+ char *fullpath;
+ char *relpath;
+ char **files;
+};
+
+STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count);
+STB_EXTERN stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count);
+STB_EXTERN int stb_dir_is_prefix(char *dir, int dirlen, char *file);
+
+#ifdef STB_DEFINE
+
+int stb_dir_is_prefix(char *dir, int dirlen, char *file)
+{
+ if (dirlen == 0) return STB_TRUE;
+ if (stb_strnicmp(dir, file, dirlen)) return STB_FALSE;
+ if (file[dirlen] == '/' || file[dirlen] == '\\') return STB_TRUE;
+ return STB_FALSE;
+}
+
+stb_dirtree2 *stb_dirtree2_from_files_relative(char *src, char **filelist, int count)
+{
+ char buffer1[1024];
+ int i;
+ int dlen = (int) strlen(src), elen;
+ stb_dirtree2 *d;
+ char ** descendents = NULL;
+ char ** files = NULL;
+ char *s;
+ if (!count) return NULL;
+ // first find all the ones that belong here... note this is will take O(NM) with N files and M subdirs
+ for (i=0; i < count; ++i) {
+ if (stb_dir_is_prefix(src, dlen, filelist[i])) {
+ stb_arr_push(descendents, filelist[i]);
+ }
+ }
+ if (descendents == NULL)
+ return NULL;
+ elen = dlen;
+ // skip a leading slash
+ if (elen == 0 && (descendents[0][0] == '/' || descendents[0][0] == '\\'))
+ ++elen;
+ else if (elen)
+ ++elen;
+ // now extract all the ones that have their root here
+ for (i=0; i < stb_arr_len(descendents);) {
+ if (!stb_strchr2(descendents[i]+elen, '/', '\\')) {
+ stb_arr_push(files, descendents[i]);
+ descendents[i] = descendents[stb_arr_len(descendents)-1];
+ stb_arr_pop(descendents);
+ } else
+ ++i;
+ }
+ // now create a record
+ d = (stb_dirtree2 *) malloc(sizeof(*d));
+ d->files = files;
+ d->subdirs = NULL;
+ d->fullpath = stb_p_strdup(src);
+ s = stb_strrchr2(d->fullpath, '/', '\\');
+ if (s)
+ ++s;
+ else
+ s = d->fullpath;
+ d->relpath = s;
+ // now create the children
+ qsort(descendents, stb_arr_len(descendents), sizeof(char *), stb_qsort_stricmp(0));
+ buffer1[0] = 0;
+ for (i=0; i < stb_arr_len(descendents); ++i) {
+ char buffer2[1024];
+ char *s = descendents[i] + elen, *t;
+ t = stb_strchr2(s, '/', '\\');
+ assert(t);
+ stb_strncpy(buffer2, descendents[i], (int) (t-descendents[i]+1));
+ if (stb_stricmp(buffer1, buffer2)) {
+ stb_dirtree2 *t = stb_dirtree2_from_files_relative(buffer2, descendents, stb_arr_len(descendents));
+ assert(t != NULL);
+ stb_p_strcpy_s(buffer1, sizeof(buffer1), buffer2);
+ stb_arr_push(d->subdirs, t);
+ }
+ }
+ d->num_subdir = stb_arr_len(d->subdirs);
+ d->weight = 0;
+ return d;
+}
+
+stb_dirtree2 *stb_dirtree2_from_files(char **filelist, int count)
+{
+ return stb_dirtree2_from_files_relative((char*) "", filelist, count);
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Checksums: CRC-32, ADLER32, SHA-1
+//
+// CRC-32 and ADLER32 allow streaming blocks
+// SHA-1 requires either a complete buffer, max size 2^32 - 73
+// or it can checksum directly from a file, max 2^61
+
+#define STB_ADLER32_SEED 1
+#define STB_CRC32_SEED 0 // note that we logical NOT this in the code
+
+STB_EXTERN stb_uint
+ stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen);
+STB_EXTERN stb_uint
+ stb_crc32_block(stb_uint crc32, stb_uchar *buffer, stb_uint len);
+STB_EXTERN stb_uint stb_crc32(unsigned char *buffer, stb_uint len);
+
+STB_EXTERN void stb_sha1(
+ unsigned char output[20], unsigned char *buffer, unsigned int len);
+STB_EXTERN int stb_sha1_file(unsigned char output[20], char *file);
+
+STB_EXTERN void stb_sha1_readable(char display[27], unsigned char sha[20]);
+
+#ifdef STB_DEFINE
+stb_uint stb_crc32_block(stb_uint crc, unsigned char *buffer, stb_uint len)
+{
+ static stb_uint crc_table[256];
+ stb_uint i,j,s;
+ crc = ~crc;
+
+ if (crc_table[1] == 0)
+ for(i=0; i < 256; i++) {
+ for (s=i, j=0; j < 8; ++j)
+ s = (s >> 1) ^ (s & 1 ? 0xedb88320 : 0);
+ crc_table[i] = s;
+ }
+ for (i=0; i < len; ++i)
+ crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)];
+ return ~crc;
+}
+
+stb_uint stb_crc32(unsigned char *buffer, stb_uint len)
+{
+ return stb_crc32_block(0, buffer, len);
+}
+
+stb_uint stb_adler32(stb_uint adler32, stb_uchar *buffer, stb_uint buflen)
+{
+ const unsigned long ADLER_MOD = 65521;
+ unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
+ unsigned long blocklen, i;
+
+ blocklen = buflen % 5552;
+ while (buflen) {
+ for (i=0; i + 7 < blocklen; i += 8) {
+ s1 += buffer[0], s2 += s1;
+ s1 += buffer[1], s2 += s1;
+ s1 += buffer[2], s2 += s1;
+ s1 += buffer[3], s2 += s1;
+ s1 += buffer[4], s2 += s1;
+ s1 += buffer[5], s2 += s1;
+ s1 += buffer[6], s2 += s1;
+ s1 += buffer[7], s2 += s1;
+
+ buffer += 8;
+ }
+
+ for (; i < blocklen; ++i)
+ s1 += *buffer++, s2 += s1;
+
+ s1 %= ADLER_MOD, s2 %= ADLER_MOD;
+ buflen -= blocklen;
+ blocklen = 5552;
+ }
+ return (s2 << 16) + s1;
+}
+
+static void stb__sha1(stb_uchar *chunk, stb_uint h[5])
+{
+ int i;
+ stb_uint a,b,c,d,e;
+ stb_uint w[80];
+
+ for (i=0; i < 16; ++i)
+ w[i] = stb_big32(&chunk[i*4]);
+ for (i=16; i < 80; ++i) {
+ stb_uint t;
+ t = w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16];
+ w[i] = (t + t) | (t >> 31);
+ }
+
+ a = h[0];
+ b = h[1];
+ c = h[2];
+ d = h[3];
+ e = h[4];
+
+ #define STB__SHA1(k,f) \
+ { \
+ stb_uint temp = (a << 5) + (a >> 27) + (f) + e + (k) + w[i]; \
+ e = d; \
+ d = c; \
+ c = (b << 30) + (b >> 2); \
+ b = a; \
+ a = temp; \
+ }
+
+ i=0;
+ for (; i < 20; ++i) STB__SHA1(0x5a827999, d ^ (b & (c ^ d)) );
+ for (; i < 40; ++i) STB__SHA1(0x6ed9eba1, b ^ c ^ d );
+ for (; i < 60; ++i) STB__SHA1(0x8f1bbcdc, (b & c) + (d & (b ^ c)) );
+ for (; i < 80; ++i) STB__SHA1(0xca62c1d6, b ^ c ^ d );
+
+ #undef STB__SHA1
+
+ h[0] += a;
+ h[1] += b;
+ h[2] += c;
+ h[3] += d;
+ h[4] += e;
+}
+
+void stb_sha1(stb_uchar output[20], stb_uchar *buffer, stb_uint len)
+{
+ unsigned char final_block[128];
+ stb_uint end_start, final_len, j;
+ int i;
+
+ stb_uint h[5];
+
+ h[0] = 0x67452301;
+ h[1] = 0xefcdab89;
+ h[2] = 0x98badcfe;
+ h[3] = 0x10325476;
+ h[4] = 0xc3d2e1f0;
+
+ // we need to write padding to the last one or two
+ // blocks, so build those first into 'final_block'
+
+ // we have to write one special byte, plus the 8-byte length
+
+ // compute the block where the data runs out
+ end_start = len & ~63;
+
+ // compute the earliest we can encode the length
+ if (((len+9) & ~63) == end_start) {
+ // it all fits in one block, so fill a second-to-last block
+ end_start -= 64;
+ }
+
+ final_len = end_start + 128;
+
+ // now we need to copy the data in
+ assert(end_start + 128 >= len+9);
+ assert(end_start < len || len < 64-9);
+
+ j = 0;
+ if (end_start > len)
+ j = (stb_uint) - (int) end_start;
+
+ for (; end_start + j < len; ++j)
+ final_block[j] = buffer[end_start + j];
+ final_block[j++] = 0x80;
+ while (j < 128-5) // 5 byte length, so write 4 extra padding bytes
+ final_block[j++] = 0;
+ // big-endian size
+ final_block[j++] = len >> 29;
+ final_block[j++] = len >> 21;
+ final_block[j++] = len >> 13;
+ final_block[j++] = len >> 5;
+ final_block[j++] = len << 3;
+ assert(j == 128 && end_start + j == final_len);
+
+ for (j=0; j < final_len; j += 64) { // 512-bit chunks
+ if (j+64 >= end_start+64)
+ stb__sha1(&final_block[j - end_start], h);
+ else
+ stb__sha1(&buffer[j], h);
+ }
+
+ for (i=0; i < 5; ++i) {
+ output[i*4 + 0] = h[i] >> 24;
+ output[i*4 + 1] = h[i] >> 16;
+ output[i*4 + 2] = h[i] >> 8;
+ output[i*4 + 3] = h[i] >> 0;
+ }
+}
+
+#ifdef _MSC_VER
+int stb_sha1_file(stb_uchar output[20], char *file)
+{
+ int i;
+ stb_uint64 length=0;
+ unsigned char buffer[128];
+
+ FILE *f = stb__fopen(file, "rb");
+ stb_uint h[5];
+
+ if (f == NULL) return 0; // file not found
+
+ h[0] = 0x67452301;
+ h[1] = 0xefcdab89;
+ h[2] = 0x98badcfe;
+ h[3] = 0x10325476;
+ h[4] = 0xc3d2e1f0;
+
+ for(;;) {
+ size_t n = fread(buffer, 1, 64, f);
+ if (n == 64) {
+ stb__sha1(buffer, h);
+ length += n;
+ } else {
+ int block = 64;
+
+ length += n;
+
+ buffer[n++] = 0x80;
+
+ // if there isn't enough room for the length, double the block
+ if (n + 8 > 64)
+ block = 128;
+
+ // pad to end
+ memset(buffer+n, 0, block-8-n);
+
+ i = block - 8;
+ buffer[i++] = (stb_uchar) (length >> 53);
+ buffer[i++] = (stb_uchar) (length >> 45);
+ buffer[i++] = (stb_uchar) (length >> 37);
+ buffer[i++] = (stb_uchar) (length >> 29);
+ buffer[i++] = (stb_uchar) (length >> 21);
+ buffer[i++] = (stb_uchar) (length >> 13);
+ buffer[i++] = (stb_uchar) (length >> 5);
+ buffer[i++] = (stb_uchar) (length << 3);
+ assert(i == block);
+ stb__sha1(buffer, h);
+ if (block == 128)
+ stb__sha1(buffer+64, h);
+ else
+ assert(block == 64);
+ break;
+ }
+ }
+ fclose(f);
+
+ for (i=0; i < 5; ++i) {
+ output[i*4 + 0] = h[i] >> 24;
+ output[i*4 + 1] = h[i] >> 16;
+ output[i*4 + 2] = h[i] >> 8;
+ output[i*4 + 3] = h[i] >> 0;
+ }
+
+ return 1;
+}
+#endif // _MSC_VER
+
+// client can truncate this wherever they like
+void stb_sha1_readable(char display[27], unsigned char sha[20])
+{
+ char encoding[65] = "0123456789abcdefghijklmnopqrstuv"
+ "wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%$";
+ int num_bits = 0, acc=0;
+ int i=0,o=0;
+ while (o < 26) {
+ int v;
+ // expand the accumulator
+ if (num_bits < 6) {
+ assert(i != 20);
+ acc += sha[i++] << num_bits;
+ num_bits += 8;
+ }
+ v = acc & ((1 << 6) - 1);
+ display[o++] = encoding[v];
+ acc >>= 6;
+ num_bits -= 6;
+ }
+ assert(num_bits == 20*8 - 26*6);
+ display[o++] = encoding[acc];
+}
+
+#endif // STB_DEFINE
+
+///////////////////////////////////////////////////////////
+//
+// simplified WINDOWS registry interface... hopefully
+// we'll never actually use this?
+
+#if defined(_WIN32)
+
+STB_EXTERN void * stb_reg_open(const char *mode, const char *where); // mode: "rHKLM" or "rHKCU" or "w.."
+STB_EXTERN void stb_reg_close(void *reg);
+STB_EXTERN int stb_reg_read(void *zreg, const char *str, void *data, unsigned long len);
+STB_EXTERN int stb_reg_read_string(void *zreg, const char *str, char *data, int len);
+STB_EXTERN void stb_reg_write(void *zreg, const char *str, const void *data, unsigned long len);
+STB_EXTERN void stb_reg_write_string(void *zreg, const char *str, const char *data);
+
+#if defined(STB_DEFINE) && !defined(STB_NO_REGISTRY)
+
+#define STB_HAS_REGISTRY
+
+#ifndef _WINDOWS_
+
+#define HKEY void *
+
+STB_EXTERN __declspec(dllimport) long __stdcall RegCloseKey ( HKEY hKey );
+STB_EXTERN __declspec(dllimport) long __stdcall RegCreateKeyExA ( HKEY hKey, const char * lpSubKey,
+ int Reserved, char * lpClass, int dwOptions,
+ int samDesired, void *lpSecurityAttributes, HKEY * phkResult, int * lpdwDisposition );
+STB_EXTERN __declspec(dllimport) long __stdcall RegDeleteKeyA ( HKEY hKey, const char * lpSubKey );
+STB_EXTERN __declspec(dllimport) long __stdcall RegQueryValueExA ( HKEY hKey, const char * lpValueName,
+ int * lpReserved, unsigned long * lpType, unsigned char * lpData, unsigned long * lpcbData );
+STB_EXTERN __declspec(dllimport) long __stdcall RegSetValueExA ( HKEY hKey, const char * lpValueName,
+ int Reserved, int dwType, const unsigned char* lpData, int cbData );
+STB_EXTERN __declspec(dllimport) long __stdcall RegOpenKeyExA ( HKEY hKey, const char * lpSubKey,
+ int ulOptions, int samDesired, HKEY * phkResult );
+
+#endif // _WINDOWS_
+
+#define STB__REG_OPTION_NON_VOLATILE 0
+#define STB__REG_KEY_ALL_ACCESS 0x000f003f
+#define STB__REG_KEY_READ 0x00020019
+
+#ifdef _M_AMD64
+#define STB__HKEY_CURRENT_USER 0x80000001ull
+#define STB__HKEY_LOCAL_MACHINE 0x80000002ull
+#else
+#define STB__HKEY_CURRENT_USER 0x80000001
+#define STB__HKEY_LOCAL_MACHINE 0x80000002
+#endif
+
+void *stb_reg_open(const char *mode, const char *where)
+{
+ long res;
+ HKEY base;
+ HKEY zreg;
+ if (!stb_stricmp(mode+1, "cu") || !stb_stricmp(mode+1, "hkcu"))
+ base = (HKEY) STB__HKEY_CURRENT_USER;
+ else if (!stb_stricmp(mode+1, "lm") || !stb_stricmp(mode+1, "hklm"))
+ base = (HKEY) STB__HKEY_LOCAL_MACHINE;
+ else
+ return NULL;
+
+ if (mode[0] == 'r')
+ res = RegOpenKeyExA(base, where, 0, STB__REG_KEY_READ, &zreg);
+ else if (mode[0] == 'w')
+ res = RegCreateKeyExA(base, where, 0, NULL, STB__REG_OPTION_NON_VOLATILE, STB__REG_KEY_ALL_ACCESS, NULL, &zreg, NULL);
+ else
+ return NULL;
+
+ return res ? NULL : zreg;
+}
+
+void stb_reg_close(void *reg)
+{
+ RegCloseKey((HKEY) reg);
+}
+
+#define STB__REG_SZ 1
+#define STB__REG_BINARY 3
+#define STB__REG_DWORD 4
+
+int stb_reg_read(void *zreg, const char *str, void *data, unsigned long len)
+{
+ unsigned long type;
+ unsigned long alen = len;
+ if (0 == RegQueryValueExA((HKEY) zreg, str, 0, &type, (unsigned char *) data, &len))
+ if (type == STB__REG_BINARY || type == STB__REG_SZ || type == STB__REG_DWORD) {
+ if (len < alen)
+ *((char *) data + len) = 0;
+ return 1;
+ }
+ return 0;
+}
+
+void stb_reg_write(void *zreg, const char *str, const void *data, unsigned long len)
+{
+ if (zreg)
+ RegSetValueExA((HKEY) zreg, str, 0, STB__REG_BINARY, (const unsigned char *) data, len);
+}
+
+int stb_reg_read_string(void *zreg, const char *str, char *data, int len)
+{
+ if (!stb_reg_read(zreg, str, data, len)) return 0;
+ data[len-1] = 0; // force a 0 at the end of the string no matter what
+ return 1;
+}
+
+void stb_reg_write_string(void *zreg, const char *str, const char *data)
+{
+ if (zreg)
+ RegSetValueExA((HKEY) zreg, str, 0, STB__REG_SZ, (const unsigned char *) data, (int) strlen(data)+1);
+}
+#endif // STB_DEFINE
+#endif // _WIN32
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_cfg - This is like the registry, but the config info
+// is all stored in plain old files where we can
+// backup and restore them easily. The LOCATION of
+// the config files is gotten from... the registry!
+
+#ifndef STB_NO_STB_STRINGS
+typedef struct stb_cfg_st stb_cfg;
+
+STB_EXTERN stb_cfg * stb_cfg_open(char *config, const char *mode); // mode = "r", "w"
+STB_EXTERN void stb_cfg_close(stb_cfg *cfg);
+STB_EXTERN int stb_cfg_read(stb_cfg *cfg, char *key, void *value, int len);
+STB_EXTERN void stb_cfg_write(stb_cfg *cfg, char *key, void *value, int len);
+STB_EXTERN int stb_cfg_read_string(stb_cfg *cfg, char *key, char *value, int len);
+STB_EXTERN void stb_cfg_write_string(stb_cfg *cfg, char *key, char *value);
+STB_EXTERN int stb_cfg_delete(stb_cfg *cfg, char *key);
+STB_EXTERN void stb_cfg_set_directory(char *dir);
+
+#ifdef STB_DEFINE
+
+typedef struct
+{
+ char *key;
+ void *value;
+ int value_len;
+} stb__cfg_item;
+
+struct stb_cfg_st
+{
+ stb__cfg_item *data;
+ char *loaded_file; // this needs to be freed
+ FILE *f; // write the data to this file on close
+};
+
+static const char *stb__cfg_sig = "sTbCoNfIg!\0\0";
+static char stb__cfg_dir[512];
+STB_EXTERN void stb_cfg_set_directory(char *dir)
+{
+ stb_p_strcpy_s(stb__cfg_dir, sizeof(stb__cfg_dir), dir);
+}
+
+STB_EXTERN stb_cfg * stb_cfg_open(char *config, const char *mode)
+{
+ size_t len;
+ stb_cfg *z;
+ char file[512];
+ if (mode[0] != 'r' && mode[0] != 'w') return NULL;
+
+ if (!stb__cfg_dir[0]) {
+ #ifdef _WIN32
+ stb_p_strcpy_s(stb__cfg_dir, sizeof(stb__cfg_dir), "c:/stb");
+ #else
+ strcpy(stb__cfg_dir, "~/.stbconfig");
+ #endif
+
+ #ifdef STB_HAS_REGISTRY
+ {
+ void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb");
+ if (reg) {
+ stb_reg_read_string(reg, "config_dir", stb__cfg_dir, sizeof(stb__cfg_dir));
+ stb_reg_close(reg);
+ }
+ }
+ #endif
+ }
+
+ stb_p_sprintf(file stb_p_size(sizeof(file)), "%s/%s.cfg", stb__cfg_dir, config);
+
+ z = (stb_cfg *) stb_malloc(0, sizeof(*z));
+ z->data = NULL;
+
+ z->loaded_file = stb_filec(file, &len);
+ if (z->loaded_file) {
+ char *s = z->loaded_file;
+ if (!memcmp(s, stb__cfg_sig, 12)) {
+ char *s = z->loaded_file + 12;
+ while (s < z->loaded_file + len) {
+ stb__cfg_item a;
+ int n = *(stb_int16 *) s;
+ a.key = s+2;
+ s = s+2 + n;
+ a.value_len = *(int *) s;
+ s += 4;
+ a.value = s;
+ s += a.value_len;
+ stb_arr_push(z->data, a);
+ }
+ assert(s == z->loaded_file + len);
+ }
+ }
+
+ if (mode[0] == 'w')
+ z->f = stb_p_fopen(file, "wb");
+ else
+ z->f = NULL;
+
+ return z;
+}
+
+void stb_cfg_close(stb_cfg *z)
+{
+ if (z->f) {
+ int i;
+ // write the file out
+ fwrite(stb__cfg_sig, 12, 1, z->f);
+ for (i=0; i < stb_arr_len(z->data); ++i) {
+ stb_int16 n = (stb_int16) strlen(z->data[i].key)+1;
+ fwrite(&n, 2, 1, z->f);
+ fwrite(z->data[i].key, n, 1, z->f);
+ fwrite(&z->data[i].value_len, 4, 1, z->f);
+ fwrite(z->data[i].value, z->data[i].value_len, 1, z->f);
+ }
+ fclose(z->f);
+ }
+ stb_arr_free(z->data);
+ stb_free(z);
+}
+
+int stb_cfg_read(stb_cfg *z, char *key, void *value, int len)
+{
+ int i;
+ for (i=0; i < stb_arr_len(z->data); ++i) {
+ if (!stb_stricmp(z->data[i].key, key)) {
+ int n = stb_min(len, z->data[i].value_len);
+ memcpy(value, z->data[i].value, n);
+ if (n < len)
+ *((char *) value + n) = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void stb_cfg_write(stb_cfg *z, char *key, void *value, int len)
+{
+ int i;
+ for (i=0; i < stb_arr_len(z->data); ++i)
+ if (!stb_stricmp(z->data[i].key, key))
+ break;
+ if (i == stb_arr_len(z->data)) {
+ stb__cfg_item p;
+ p.key = stb_strdup(key, z);
+ p.value = NULL;
+ p.value_len = 0;
+ stb_arr_push(z->data, p);
+ }
+ z->data[i].value = stb_malloc(z, len);
+ z->data[i].value_len = len;
+ memcpy(z->data[i].value, value, len);
+}
+
+int stb_cfg_delete(stb_cfg *z, char *key)
+{
+ int i;
+ for (i=0; i < stb_arr_len(z->data); ++i)
+ if (!stb_stricmp(z->data[i].key, key)) {
+ stb_arr_fastdelete(z->data, i);
+ return 1;
+ }
+ return 0;
+}
+
+int stb_cfg_read_string(stb_cfg *z, char *key, char *value, int len)
+{
+ if (!stb_cfg_read(z, key, value, len)) return 0;
+ value[len-1] = 0;
+ return 1;
+}
+
+void stb_cfg_write_string(stb_cfg *z, char *key, char *value)
+{
+ stb_cfg_write(z, key, value, (int) strlen(value)+1);
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_dirtree - load a description of a directory tree
+// uses a cache and stat()s the directories for changes
+// MUCH faster on NTFS, _wrong_ on FAT32, so should
+// ignore the db on FAT32
+
+#ifdef _WIN32
+
+typedef struct
+{
+ char * path; // full path from passed-in root
+ time_t last_modified;
+ int num_files;
+ int flag;
+} stb_dirtree_dir;
+
+typedef struct
+{
+ char *name; // name relative to path
+ int dir; // index into dirs[] array
+ stb_int64 size; // size, max 4GB
+ time_t last_modified;
+ int flag;
+} stb_dirtree_file;
+
+typedef struct
+{
+ stb_dirtree_dir *dirs;
+ stb_dirtree_file *files;
+
+ // internal use
+ void * string_pool; // used to free data en masse
+} stb_dirtree;
+
+extern void stb_dirtree_free ( stb_dirtree *d );
+extern stb_dirtree *stb_dirtree_get ( char *dir);
+extern stb_dirtree *stb_dirtree_get_dir ( char *dir, char *cache_dir);
+extern stb_dirtree *stb_dirtree_get_with_file ( char *dir, char *cache_file);
+
+// get a list of all the files recursively underneath 'dir'
+//
+// cache_file is used to store a copy of the directory tree to speed up
+// later calls. It must be unique to 'dir' and the current working
+// directory! Otherwise who knows what will happen (a good solution
+// is to put it _in_ dir, but this API doesn't force that).
+//
+// Also, it might be possible to break this if you have two different processes
+// do a call to stb_dirtree_get() with the same cache file at about the same
+// time, but I _think_ it might just work.
+
+// i needed to build an identical data structure representing the state of
+// a mirrored copy WITHOUT bothering to rescan it (i.e. we're mirroring to
+// it WITHOUT scanning it, e.g. it's over the net), so this requires access
+// to all of the innards.
+extern void stb_dirtree_db_add_dir(stb_dirtree *active, char *path, time_t last);
+extern void stb_dirtree_db_add_file(stb_dirtree *active, char *name, int dir, stb_int64 size, time_t last);
+extern void stb_dirtree_db_read(stb_dirtree *target, char *filename, char *dir);
+extern void stb_dirtree_db_write(stb_dirtree *target, char *filename, char *dir);
+
+#ifdef STB_DEFINE
+static void stb__dirtree_add_dir(char *path, time_t last, stb_dirtree *active)
+{
+ stb_dirtree_dir d;
+ d.last_modified = last;
+ d.num_files = 0;
+ d.path = stb_strdup(path, active->string_pool);
+ stb_arr_push(active->dirs, d);
+}
+
+static void stb__dirtree_add_file(char *name, int dir, stb_int64 size, time_t last, stb_dirtree *active)
+{
+ stb_dirtree_file f;
+ f.dir = dir;
+ f.size = size;
+ f.last_modified = last;
+ f.name = stb_strdup(name, active->string_pool);
+ ++active->dirs[dir].num_files;
+ stb_arr_push(active->files, f);
+}
+
+// version 02 supports > 4GB files
+static char stb__signature[12] = { 's', 'T', 'b', 'D', 'i', 'R', 't', 'R', 'e', 'E', '0', '2' };
+
+static void stb__dirtree_save_db(char *filename, stb_dirtree *data, char *root)
+{
+ int i, num_dirs_final=0, num_files_final;
+ char *info = root ? root : (char*)"";
+ int *remap;
+ FILE *f = stb_p_fopen(filename, "wb");
+ if (!f) return;
+
+ fwrite(stb__signature, sizeof(stb__signature), 1, f);
+ fwrite(info, strlen(info)+1, 1, f);
+ // need to be slightly tricky and not write out NULLed directories, nor the root
+
+ // build remapping table of all dirs we'll be writing out
+ remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(data->dirs));
+ for (i=0; i < stb_arr_len(data->dirs); ++i) {
+ if (data->dirs[i].path == NULL || (root && 0==stb_stricmp(data->dirs[i].path, root))) {
+ remap[i] = -1;
+ } else {
+ remap[i] = num_dirs_final++;
+ }
+ }
+
+ fwrite(&num_dirs_final, 4, 1, f);
+ for (i=0; i < stb_arr_len(data->dirs); ++i) {
+ if (remap[i] >= 0) {
+ fwrite(&data->dirs[i].last_modified, 4, 1, f);
+ stb_fput_string(f, data->dirs[i].path);
+ }
+ }
+
+ num_files_final = 0;
+ for (i=0; i < stb_arr_len(data->files); ++i)
+ if (remap[data->files[i].dir] >= 0 && data->files[i].name)
+ ++num_files_final;
+
+ fwrite(&num_files_final, 4, 1, f);
+ for (i=0; i < stb_arr_len(data->files); ++i) {
+ if (remap[data->files[i].dir] >= 0 && data->files[i].name) {
+ stb_fput_ranged(f, remap[data->files[i].dir], 0, num_dirs_final);
+ stb_fput_varlen64(f, data->files[i].size);
+ fwrite(&data->files[i].last_modified, 4, 1, f);
+ stb_fput_string(f, data->files[i].name);
+ }
+ }
+
+ fclose(f);
+}
+
+// note: stomps any existing data, rather than appending
+static void stb__dirtree_load_db(char *filename, stb_dirtree *data, char *dir)
+{
+ char sig[2048];
+ int i,n;
+ FILE *f = stb_p_fopen(filename, "rb");
+
+ if (!f) return;
+
+ data->string_pool = stb_malloc(0,1);
+
+ fread(sig, sizeof(stb__signature), 1, f);
+ if (memcmp(stb__signature, sig, sizeof(stb__signature))) { fclose(f); return; }
+ if (!fread(sig, strlen(dir)+1, 1, f)) { fclose(f); return; }
+ if (stb_stricmp(sig,dir)) { fclose(f); return; }
+
+ // we can just read them straight in, because they're guaranteed to be valid
+ fread(&n, 4, 1, f);
+ stb_arr_setlen(data->dirs, n);
+ for(i=0; i < stb_arr_len(data->dirs); ++i) {
+ fread(&data->dirs[i].last_modified, 4, 1, f);
+ data->dirs[i].path = stb_fget_string(f, data->string_pool);
+ if (data->dirs[i].path == NULL) goto bail;
+ }
+ fread(&n, 4, 1, f);
+ stb_arr_setlen(data->files, n);
+ for (i=0; i < stb_arr_len(data->files); ++i) {
+ data->files[i].dir = stb_fget_ranged(f, 0, stb_arr_len(data->dirs));
+ data->files[i].size = stb_fget_varlen64(f);
+ fread(&data->files[i].last_modified, 4, 1, f);
+ data->files[i].name = stb_fget_string(f, data->string_pool);
+ if (data->files[i].name == NULL) goto bail;
+ }
+
+ if (0) {
+ bail:
+ stb_arr_free(data->dirs);
+ stb_arr_free(data->files);
+ }
+ fclose(f);
+}
+
+FILE *hlog;
+
+static int stb__dircount, stb__dircount_mask, stb__showfile;
+static void stb__dirtree_scandir(char *path, time_t last_time, stb_dirtree *active)
+{
+ // this is dumb depth first; theoretically it might be faster
+ // to fully traverse each directory before visiting its children,
+ // but it's complicated and didn't seem like a gain in the test app
+
+ int n;
+
+ struct _wfinddatai64_t c_file;
+ long hFile;
+ stb__wchar full_path[1024];
+ int has_slash;
+ if (stb__showfile) printf("<");
+
+ has_slash = (path[0] && path[strlen(path)-1] == '/');
+
+ // @TODO: do this concatenation without using swprintf to avoid this mess:
+#if (defined(_MSC_VER) && _MSC_VER < 1400) // || (defined(__clang__))
+ // confusingly, Windows Kits\10 needs to go down this path?!?
+ // except now it doesn't, I don't know what changed
+ if (has_slash)
+ swprintf(full_path, L"%s*", stb__from_utf8(path));
+ else
+ swprintf(full_path, L"%s/*", stb__from_utf8(path));
+#else
+ if (has_slash)
+ swprintf((wchar_t *) full_path, (size_t) 1024, L"%s*", (wchar_t *) stb__from_utf8(path));
+ else
+ swprintf((wchar_t *) full_path, (size_t) 1024, L"%s/*", (wchar_t *) stb__from_utf8(path));
+#endif
+
+ // it's possible this directory is already present: that means it was in the
+ // cache, but its parent wasn't... in that case, we're done with it
+ if (stb__showfile) printf("C[%d]", stb_arr_len(active->dirs));
+ for (n=0; n < stb_arr_len(active->dirs); ++n)
+ if (0 == stb_stricmp(active->dirs[n].path, path)) {
+ if (stb__showfile) printf("D");
+ return;
+ }
+ if (stb__showfile) printf("E");
+
+ // otherwise, we need to add it
+ stb__dirtree_add_dir(path, last_time, active);
+ n = stb_arr_lastn(active->dirs);
+
+ if (stb__showfile) printf("[");
+ if( (hFile = (long) _wfindfirsti64( (wchar_t *) full_path, &c_file )) != -1L ) {
+ do {
+ if (stb__showfile) printf(")");
+ if (c_file.attrib & _A_SUBDIR) {
+ // ignore subdirectories starting with '.', e.g. "." and ".."
+ if (c_file.name[0] != '.') {
+ char *new_path = (char *) full_path;
+ char *temp = stb__to_utf8((stb__wchar *) c_file.name);
+
+ if (has_slash)
+ stb_p_sprintf(new_path stb_p_size(sizeof(full_path)), "%s%s", path, temp);
+ else
+ stb_p_sprintf(new_path stb_p_size(sizeof(full_path)), "%s/%s", path, temp);
+
+ if (stb__dircount_mask) {
+ ++stb__dircount;
+ if (!(stb__dircount & stb__dircount_mask)) {
+ char dummy_path[128], *pad;
+ stb_strncpy(dummy_path, new_path, sizeof(dummy_path)-1);
+ if (strlen(dummy_path) > 96) {
+ stb_p_strcpy_s(dummy_path+96/2-1,128, "...");
+ stb_p_strcpy_s(dummy_path+96/2+2,128, new_path + strlen(new_path)-96/2+2);
+ }
+ pad = dummy_path + strlen(dummy_path);
+ while (pad < dummy_path+98)
+ *pad++ = ' ';
+ *pad = 0;
+ printf("%s\r", dummy_path);
+ #if 0
+ if (hlog == 0) {
+ hlog = stb_p_fopen("c:/x/temp.log", "w");
+ fprintf(hlog, "%s\n", dummy_path);
+ }
+ #endif
+ }
+ }
+
+ stb__dirtree_scandir(new_path, c_file.time_write, active);
+ }
+ } else {
+ char *temp = stb__to_utf8((stb__wchar *) c_file.name);
+ stb__dirtree_add_file(temp, n, c_file.size, c_file.time_write, active);
+ }
+ if (stb__showfile) printf("(");
+ } while( _wfindnexti64( hFile, &c_file ) == 0 );
+ if (stb__showfile) printf("]");
+ _findclose( hFile );
+ }
+ if (stb__showfile) printf(">\n");
+}
+
+// scan the database and see if it's all valid
+static int stb__dirtree_update_db(stb_dirtree *db, stb_dirtree *active)
+{
+ int changes_detected = STB_FALSE;
+ int i;
+ int *remap;
+ int *rescan=NULL;
+ remap = (int *) malloc(sizeof(remap[0]) * stb_arr_len(db->dirs));
+ memset(remap, 0, sizeof(remap[0]) * stb_arr_len(db->dirs));
+ rescan = NULL;
+
+ for (i=0; i < stb_arr_len(db->dirs); ++i) {
+ struct _stat info;
+ if (stb__dircount_mask) {
+ ++stb__dircount;
+ if (!(stb__dircount & stb__dircount_mask)) {
+ printf(".");
+ }
+ }
+ if (0 == _stat(db->dirs[i].path, &info)) {
+ if (info.st_mode & _S_IFDIR) {
+ // it's still a directory, as expected
+ int n = abs((int) (info.st_mtime - db->dirs[i].last_modified));
+ if (n > 1 && n != 3600) { // the 3600 is a hack because sometimes this jumps for no apparent reason, even when no time zone or DST issues are at play
+ // it's changed! force a rescan
+ // we don't want to scan it until we've stat()d its
+ // subdirs, though, so we queue it
+ if (stb__showfile) printf("Changed: %s - %08x:%08x\n", db->dirs[i].path, (unsigned int) db->dirs[i].last_modified, (unsigned int) info.st_mtime);
+ stb_arr_push(rescan, i);
+ // update the last_mod time
+ db->dirs[i].last_modified = info.st_mtime;
+ // ignore existing files in this dir
+ remap[i] = -1;
+ changes_detected = STB_TRUE;
+ } else {
+ // it hasn't changed, just copy it through unchanged
+ stb__dirtree_add_dir(db->dirs[i].path, db->dirs[i].last_modified, active);
+ remap[i] = stb_arr_lastn(active->dirs);
+ }
+ } else {
+ // this path used to refer to a directory, but now it's a file!
+ // assume that the parent directory is going to be forced to rescan anyway
+ goto delete_entry;
+ }
+ } else {
+ delete_entry:
+ // directory no longer exists, so don't copy it
+ // we don't free it because it's in the string pool now
+ db->dirs[i].path = NULL;
+ remap[i] = -1;
+ changes_detected = STB_TRUE;
+ }
+ }
+
+ // at this point, we have:
+ //
+ // <rescan> holds a list of directory indices that need to be scanned due to being out of date
+ // <remap> holds the directory index in <active> for each dir in <db>, if it exists; -1 if not
+ // directories in <rescan> are not in <active> yet
+
+ // so we can go ahead and remap all the known files right now
+ for (i=0; i < stb_arr_len(db->files); ++i) {
+ int dir = db->files[i].dir;
+ if (remap[dir] >= 0) {
+ stb__dirtree_add_file(db->files[i].name, remap[dir], db->files[i].size, db->files[i].last_modified, active);
+ }
+ }
+
+ // at this point we're done with db->files, and done with remap
+ free(remap);
+
+ // now scan those directories using the standard scan
+ for (i=0; i < stb_arr_len(rescan); ++i) {
+ int z = rescan[i];
+ stb__dirtree_scandir(db->dirs[z].path, db->dirs[z].last_modified, active);
+ }
+ stb_arr_free(rescan);
+
+ return changes_detected;
+}
+
+static void stb__dirtree_free_raw(stb_dirtree *d)
+{
+ stb_free(d->string_pool);
+ stb_arr_free(d->dirs);
+ stb_arr_free(d->files);
+}
+
+stb_dirtree *stb_dirtree_get_with_file(char *dir, char *cache_file)
+{
+ stb_dirtree *output = (stb_dirtree *) malloc(sizeof(*output));
+ stb_dirtree db,active;
+ int prev_dir_count, cache_mismatch;
+
+ char *stripped_dir; // store the directory name without a trailing '/' or '\\'
+
+ // load the database of last-known state on disk
+ db.string_pool = NULL;
+ db.files = NULL;
+ db.dirs = NULL;
+
+ stripped_dir = stb_strip_final_slash(stb_p_strdup(dir));
+
+ if (cache_file != NULL)
+ stb__dirtree_load_db(cache_file, &db, stripped_dir);
+ else if (stb__showfile)
+ printf("No cache file\n");
+
+ active.files = NULL;
+ active.dirs = NULL;
+ active.string_pool = stb_malloc(0,1); // @TODO: share string pools between both?
+
+ // check all the directories in the database; make note if
+ // anything we scanned had changed, and rescan those things
+ cache_mismatch = stb__dirtree_update_db(&db, &active);
+
+ // check the root tree
+ prev_dir_count = stb_arr_len(active.dirs); // record how many directories we've seen
+
+ stb__dirtree_scandir(stripped_dir, 0, &active); // no last_modified time available for root
+
+ if (stb__dircount_mask)
+ printf(" \r");
+
+ // done with the DB; write it back out if any changes, i.e. either
+ // 1. any inconsistency found between cached information and actual disk
+ // or 2. if scanning the root found any new directories--which we detect because
+ // more than one directory got added to the active db during that scan
+ if (cache_mismatch || stb_arr_len(active.dirs) > prev_dir_count+1)
+ stb__dirtree_save_db(cache_file, &active, stripped_dir);
+
+ free(stripped_dir);
+
+ stb__dirtree_free_raw(&db);
+
+ *output = active;
+ return output;
+}
+
+stb_dirtree *stb_dirtree_get_dir(char *dir, char *cache_dir)
+{
+ int i;
+ stb_uint8 sha[20];
+ char dir_lower[1024];
+ char cache_file[1024],*s;
+ if (cache_dir == NULL)
+ return stb_dirtree_get_with_file(dir, NULL);
+ stb_p_strcpy_s(dir_lower, sizeof(dir_lower), dir);
+ stb_tolower(dir_lower);
+ stb_sha1(sha, (unsigned char *) dir_lower, (unsigned int) strlen(dir_lower));
+ stb_p_strcpy_s(cache_file, sizeof(cache_file), cache_dir);
+ s = cache_file + strlen(cache_file);
+ if (s[-1] != '/' && s[-1] != '\\') *s++ = '/';
+ stb_p_strcpy_s(s, sizeof(cache_file), "dirtree_");
+ s += strlen(s);
+ for (i=0; i < 8; ++i) {
+ char *hex = (char*)"0123456789abcdef";
+ stb_uint z = sha[i];
+ *s++ = hex[z >> 4];
+ *s++ = hex[z & 15];
+ }
+ stb_p_strcpy_s(s, sizeof(cache_file), ".bin");
+ return stb_dirtree_get_with_file(dir, cache_file);
+}
+
+stb_dirtree *stb_dirtree_get(char *dir)
+{
+ char cache_dir[256];
+ stb_p_strcpy_s(cache_dir, sizeof(cache_dir), "c:/bindata");
+ #ifdef STB_HAS_REGISTRY
+ {
+ void *reg = stb_reg_open("rHKLM", "Software\\SilverSpaceship\\stb");
+ if (reg) {
+ stb_reg_read(reg, "dirtree", cache_dir, sizeof(cache_dir));
+ stb_reg_close(reg);
+ }
+ }
+ #endif
+ return stb_dirtree_get_dir(dir, cache_dir);
+}
+
+void stb_dirtree_free(stb_dirtree *d)
+{
+ stb__dirtree_free_raw(d);
+ free(d);
+}
+
+void stb_dirtree_db_add_dir(stb_dirtree *active, char *path, time_t last)
+{
+ stb__dirtree_add_dir(path, last, active);
+}
+
+void stb_dirtree_db_add_file(stb_dirtree *active, char *name, int dir, stb_int64 size, time_t last)
+{
+ stb__dirtree_add_file(name, dir, size, last, active);
+}
+
+void stb_dirtree_db_read(stb_dirtree *target, char *filename, char *dir)
+{
+ char *s = stb_strip_final_slash(stb_p_strdup(dir));
+ target->dirs = 0;
+ target->files = 0;
+ target->string_pool = 0;
+ stb__dirtree_load_db(filename, target, s);
+ free(s);
+}
+
+void stb_dirtree_db_write(stb_dirtree *target, char *filename, char *dir)
+{
+ stb__dirtree_save_db(filename, target, 0); // don't strip out any directories
+}
+
+#endif // STB_DEFINE
+
+#endif // _WIN32
+#endif // STB_NO_STB_STRINGS
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// STB_MALLOC_WRAPPER
+//
+// you can use the wrapper functions with your own malloc wrapper,
+// or define STB_MALLOC_WRAPPER project-wide to have
+// malloc/free/realloc/strdup all get vectored to it
+
+// this has too many very specific error messages you could google for and find in stb.h,
+// so don't use it if they don't want any stb.h-identifiable strings
+#if defined(STB_DEFINE) && !defined(STB_NO_STB_STRINGS)
+
+typedef struct
+{
+ void *p;
+ char *file;
+ int line;
+ size_t size;
+} stb_malloc_record;
+
+#ifndef STB_MALLOC_HISTORY_COUNT
+#define STB_MALLOC_HISTORY_COUNT 50 // 800 bytes
+#endif
+
+stb_malloc_record *stb__allocations;
+static int stb__alloc_size, stb__alloc_limit, stb__alloc_mask;
+int stb__alloc_count;
+
+stb_malloc_record stb__alloc_history[STB_MALLOC_HISTORY_COUNT];
+int stb__history_pos;
+
+static int stb__hashfind(void *p)
+{
+ stb_uint32 h = stb_hashptr(p);
+ int s,n = h & stb__alloc_mask;
+ if (stb__allocations[n].p == p)
+ return n;
+ s = stb_rehash(h)|1;
+ for(;;) {
+ if (stb__allocations[n].p == NULL)
+ return -1;
+ n = (n+s) & stb__alloc_mask;
+ if (stb__allocations[n].p == p)
+ return n;
+ }
+}
+
+size_t stb_wrapper_allocsize(void *p)
+{
+ int n = stb__hashfind(p);
+ if (n < 0) return 0;
+ return stb__allocations[n].size;
+}
+
+static int stb__historyfind(void *p)
+{
+ int n = stb__history_pos;
+ int i;
+ for (i=0; i < STB_MALLOC_HISTORY_COUNT; ++i) {
+ if (--n < 0) n = STB_MALLOC_HISTORY_COUNT-1;
+ if (stb__alloc_history[n].p == p)
+ return n;
+ }
+ return -1;
+}
+
+static void stb__add_alloc(void *p, size_t sz, char *file, int line);
+static void stb__grow_alloc(void)
+{
+ int i,old_num = stb__alloc_size;
+ stb_malloc_record *old = stb__allocations;
+ if (stb__alloc_size == 0)
+ stb__alloc_size = 64;
+ else
+ stb__alloc_size *= 2;
+
+ stb__allocations = (stb_malloc_record *) stb__realloc_raw(NULL, stb__alloc_size * sizeof(stb__allocations[0]));
+ if (stb__allocations == NULL)
+ stb_fatal("Internal error: couldn't grow malloc wrapper table");
+ memset(stb__allocations, 0, stb__alloc_size * sizeof(stb__allocations[0]));
+ stb__alloc_limit = (stb__alloc_size*3)>>2;
+ stb__alloc_mask = stb__alloc_size-1;
+
+ stb__alloc_count = 0;
+
+ for (i=0; i < old_num; ++i)
+ if (old[i].p > STB_DEL) {
+ stb__add_alloc(old[i].p, old[i].size, old[i].file, old[i].line);
+ assert(stb__hashfind(old[i].p) >= 0);
+ }
+ for (i=0; i < old_num; ++i)
+ if (old[i].p > STB_DEL)
+ assert(stb__hashfind(old[i].p) >= 0);
+ stb__realloc_raw(old, 0);
+}
+
+static void stb__add_alloc(void *p, size_t sz, char *file, int line)
+{
+ stb_uint32 h;
+ int n;
+ if (stb__alloc_count >= stb__alloc_limit)
+ stb__grow_alloc();
+ h = stb_hashptr(p);
+ n = h & stb__alloc_mask;
+ if (stb__allocations[n].p > STB_DEL) {
+ int s = stb_rehash(h)|1;
+ do {
+ n = (n+s) & stb__alloc_mask;
+ } while (stb__allocations[n].p > STB_DEL);
+ }
+ assert(stb__allocations[n].p == NULL || stb__allocations[n].p == STB_DEL);
+ stb__allocations[n].p = p;
+ stb__allocations[n].size = sz;
+ stb__allocations[n].line = line;
+ stb__allocations[n].file = file;
+ ++stb__alloc_count;
+}
+
+static void stb__remove_alloc(int n, char *file, int line)
+{
+ stb__alloc_history[stb__history_pos] = stb__allocations[n];
+ stb__alloc_history[stb__history_pos].file = file;
+ stb__alloc_history[stb__history_pos].line = line;
+ if (++stb__history_pos == STB_MALLOC_HISTORY_COUNT)
+ stb__history_pos = 0;
+ stb__allocations[n].p = STB_DEL;
+ --stb__alloc_count;
+}
+
+void stb_wrapper_malloc(void *p, size_t sz, char *file, int line)
+{
+ if (!p) return;
+ stb__add_alloc(p,sz,file,line);
+}
+
+void stb_wrapper_free(void *p, char *file, int line)
+{
+ int n;
+
+ if (p == NULL) return;
+
+ n = stb__hashfind(p);
+
+ if (n >= 0)
+ stb__remove_alloc(n, file, line);
+ else {
+ // tried to free something we hadn't allocated!
+ n = stb__historyfind(p);
+ assert(0); /* NOTREACHED */
+ if (n >= 0)
+ stb_fatal("Attempted to free %d-byte block %p at %s:%d previously freed/realloced at %s:%d",
+ stb__alloc_history[n].size, p,
+ file, line,
+ stb__alloc_history[n].file, stb__alloc_history[n].line);
+ else
+ stb_fatal("Attempted to free unknown block %p at %s:%d", p, file,line);
+ }
+}
+
+void stb_wrapper_check(void *p)
+{
+ int n;
+
+ if (p == NULL) return;
+
+ n = stb__hashfind(p);
+
+ if (n >= 0) return;
+
+ for (n=0; n < stb__alloc_size; ++n)
+ if (stb__allocations[n].p == p)
+ stb_fatal("Internal error: pointer %p was allocated, but hash search failed", p);
+
+ // tried to free something that wasn't allocated!
+ n = stb__historyfind(p);
+ if (n >= 0)
+ stb_fatal("Checked %d-byte block %p previously freed/realloced at %s:%d",
+ stb__alloc_history[n].size, p,
+ stb__alloc_history[n].file, stb__alloc_history[n].line);
+ stb_fatal("Checked unknown block %p");
+}
+
+void stb_wrapper_realloc(void *p, void *q, size_t sz, char *file, int line)
+{
+ int n;
+ if (p == NULL) { stb_wrapper_malloc(q, sz, file, line); return; }
+ if (q == NULL) return; // nothing happened
+
+ n = stb__hashfind(p);
+ if (n == -1) {
+ // tried to free something we hadn't allocated!
+ // this is weird, though, because we got past the realloc!
+ n = stb__historyfind(p);
+ assert(0); /* NOTREACHED */
+ if (n >= 0)
+ stb_fatal("Attempted to realloc %d-byte block %p at %s:%d previously freed/realloced at %s:%d",
+ stb__alloc_history[n].size, p,
+ file, line,
+ stb__alloc_history[n].file, stb__alloc_history[n].line);
+ else
+ stb_fatal("Attempted to realloc unknown block %p at %s:%d", p, file,line);
+ } else {
+ if (q == p) {
+ stb__allocations[n].size = sz;
+ stb__allocations[n].file = file;
+ stb__allocations[n].line = line;
+ } else {
+ stb__remove_alloc(n, file, line);
+ stb__add_alloc(q,sz,file,line);
+ }
+ }
+}
+
+void stb_wrapper_listall(void (*func)(void *ptr, size_t sz, char *file, int line))
+{
+ int i;
+ for (i=0; i < stb__alloc_size; ++i)
+ if (stb__allocations[i].p > STB_DEL)
+ func(stb__allocations[i].p , stb__allocations[i].size,
+ stb__allocations[i].file, stb__allocations[i].line);
+}
+
+void stb_wrapper_dump(char *filename)
+{
+ int i;
+ FILE *f = stb_p_fopen(filename, "w");
+ if (!f) return;
+ for (i=0; i < stb__alloc_size; ++i)
+ if (stb__allocations[i].p > STB_DEL)
+ fprintf(f, "%p %7d - %4d %s\n",
+ stb__allocations[i].p , (int) stb__allocations[i].size,
+ stb__allocations[i].line, stb__allocations[i].file);
+}
+#endif // STB_DEFINE
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_pointer_set
+//
+//
+// For data structures that support querying by key, data structure
+// classes always hand-wave away the issue of what to do if two entries
+// have the same key: basically, store a linked list of all the nodes
+// which have the same key (a LISP-style list).
+//
+// The thing is, it's not that trivial. If you have an O(log n)
+// lookup data structure, but then n/4 items have the same value,
+// you don't want to spend O(n) time scanning that list when
+// deleting an item if you already have a pointer to the item.
+// (You have to spend O(n) time enumerating all the items with
+// a given key, sure, and you can't accelerate deleting a particular
+// item if you only have the key, not a pointer to the item.)
+//
+// I'm going to call this data structure, whatever it turns out to
+// be, a "pointer set", because we don't store any associated data for
+// items in this data structure, we just answer the question of
+// whether an item is in it or not (it's effectively one bit per pointer).
+// Technically they don't have to be pointers; you could cast ints
+// to (void *) if you want, but you can't store 0 or 1 because of the
+// hash table.
+//
+// Since the fastest data structure we might want to add support for
+// identical-keys to is a hash table with O(1)-ish lookup time,
+// that means that the conceptual "linked list of all items with
+// the same indexed value" that we build needs to have the same
+// performance; that way when we index a table we think is arbitrary
+// ints, but in fact half of them are 0, we don't get screwed.
+//
+// Therefore, it needs to be a hash table, at least when it gets
+// large. On the other hand, when the data has totally arbitrary ints
+// or floats, there won't be many collisions, and we'll have tons of
+// 1-item bitmaps. That will be grossly inefficient as hash tables;
+// trade-off; the hash table is reasonably efficient per-item when
+// it's large, but not when it's small. So we need to do something
+// Judy-like and use different strategies depending on the size.
+//
+// Like Judy, we'll use the bottom bit to encode the strategy:
+//
+// bottom bits:
+// 00 - direct pointer
+// 01 - 4-item bucket (16 bytes, no length, NULLs)
+// 10 - N-item array
+// 11 - hash table
+
+typedef struct stb_ps stb_ps;
+
+STB_EXTERN int stb_ps_find (stb_ps *ps, void *value);
+STB_EXTERN stb_ps * stb_ps_add (stb_ps *ps, void *value);
+STB_EXTERN stb_ps * stb_ps_remove(stb_ps *ps, void *value);
+STB_EXTERN stb_ps * stb_ps_remove_any(stb_ps *ps, void **value);
+STB_EXTERN void stb_ps_delete(stb_ps *ps);
+STB_EXTERN int stb_ps_count (stb_ps *ps);
+
+STB_EXTERN stb_ps * stb_ps_copy (stb_ps *ps);
+STB_EXTERN int stb_ps_subset(stb_ps *bigger, stb_ps *smaller);
+STB_EXTERN int stb_ps_eq (stb_ps *p0, stb_ps *p1);
+
+STB_EXTERN void ** stb_ps_getlist (stb_ps *ps, int *count);
+STB_EXTERN int stb_ps_writelist(stb_ps *ps, void **list, int size );
+
+// enum and fastlist don't allocate storage, but you must consume the
+// list before there's any chance the data structure gets screwed up;
+STB_EXTERN int stb_ps_enum (stb_ps *ps, void *data,
+ int (*func)(void *value, void*data) );
+STB_EXTERN void ** stb_ps_fastlist(stb_ps *ps, int *count);
+// result:
+// returns a list, *count is the length of that list,
+// but some entries of the list may be invalid;
+// test with 'stb_ps_fastlist_valid(x)'
+
+#define stb_ps_fastlist_valid(x) ((stb_uinta) (x) > 1)
+
+#ifdef STB_DEFINE
+
+enum
+{
+ STB_ps_direct = 0,
+ STB_ps_bucket = 1,
+ STB_ps_array = 2,
+ STB_ps_hash = 3,
+};
+
+#define STB_BUCKET_SIZE 4
+
+typedef struct
+{
+ void *p[STB_BUCKET_SIZE];
+} stb_ps_bucket;
+#define GetBucket(p) ((stb_ps_bucket *) ((char *) (p) - STB_ps_bucket))
+#define EncodeBucket(p) ((stb_ps *) ((char *) (p) + STB_ps_bucket))
+
+static void stb_bucket_free(stb_ps_bucket *b)
+{
+ free(b);
+}
+
+static stb_ps_bucket *stb_bucket_create2(void *v0, void *v1)
+{
+ stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b));
+ b->p[0] = v0;
+ b->p[1] = v1;
+ b->p[2] = NULL;
+ b->p[3] = NULL;
+ return b;
+}
+
+static stb_ps_bucket * stb_bucket_create3(void **v)
+{
+ stb_ps_bucket *b = (stb_ps_bucket*) malloc(sizeof(*b));
+ b->p[0] = v[0];
+ b->p[1] = v[1];
+ b->p[2] = v[2];
+ b->p[3] = NULL;
+ return b;
+}
+
+
+// could use stb_arr, but this will save us memory
+typedef struct
+{
+ int count;
+ void *p[1];
+} stb_ps_array;
+#define GetArray(p) ((stb_ps_array *) ((char *) (p) - STB_ps_array))
+#define EncodeArray(p) ((stb_ps *) ((char *) (p) + STB_ps_array))
+
+static int stb_ps_array_max = 13;
+
+typedef struct
+{
+ int size, mask;
+ int count, count_deletes;
+ int grow_threshhold;
+ int shrink_threshhold;
+ int rehash_threshhold;
+ int any_offset;
+ void *table[1];
+} stb_ps_hash;
+#define GetHash(p) ((stb_ps_hash *) ((char *) (p) - STB_ps_hash))
+#define EncodeHash(p) ((stb_ps *) ((char *) (p) + STB_ps_hash))
+
+#define stb_ps_empty(v) (((stb_uint32) v) <= 1)
+
+static stb_ps_hash *stb_ps_makehash(int size, int old_size, void **old_data)
+{
+ int i;
+ stb_ps_hash *h = (stb_ps_hash *) malloc(sizeof(*h) + (size-1) * sizeof(h->table[0]));
+ assert(stb_is_pow2(size));
+ h->size = size;
+ h->mask = size-1;
+ h->shrink_threshhold = (int) (0.3f * size);
+ h-> grow_threshhold = (int) (0.8f * size);
+ h->rehash_threshhold = (int) (0.9f * size);
+ h->count = 0;
+ h->count_deletes = 0;
+ h->any_offset = 0;
+ memset(h->table, 0, size * sizeof(h->table[0]));
+ for (i=0; i < old_size; ++i)
+ if (!stb_ps_empty((size_t)old_data[i]))
+ stb_ps_add(EncodeHash(h), old_data[i]);
+ return h;
+}
+
+void stb_ps_delete(stb_ps *ps)
+{
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct: break;
+ case STB_ps_bucket: stb_bucket_free(GetBucket(ps)); break;
+ case STB_ps_array : free(GetArray(ps)); break;
+ case STB_ps_hash : free(GetHash(ps)); break;
+ }
+}
+
+stb_ps *stb_ps_copy(stb_ps *ps)
+{
+ int i;
+ // not a switch: order based on expected performance/power-law distribution
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct: return ps;
+ case STB_ps_bucket: {
+ stb_ps_bucket *n = (stb_ps_bucket *) malloc(sizeof(*n));
+ *n = *GetBucket(ps);
+ return EncodeBucket(n);
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ stb_ps_array *n = (stb_ps_array *) malloc(sizeof(*n) + stb_ps_array_max * sizeof(n->p[0]));
+ n->count = a->count;
+ for (i=0; i < a->count; ++i)
+ n->p[i] = a->p[i];
+ return EncodeArray(n);
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ stb_ps_hash *n = stb_ps_makehash(h->size, h->size, h->table);
+ return EncodeHash(n);
+ }
+ }
+ assert(0); /* NOTREACHED */
+ return NULL;
+}
+
+int stb_ps_find(stb_ps *ps, void *value)
+{
+ int i, code = 3 & (int)(size_t) ps;
+ assert((3 & (int)(size_t) value) == STB_ps_direct);
+ assert(stb_ps_fastlist_valid(value));
+ // not a switch: order based on expected performance/power-law distribution
+ if (code == STB_ps_direct)
+ return value == ps;
+ if (code == STB_ps_bucket) {
+ stb_ps_bucket *b = GetBucket(ps);
+ assert(STB_BUCKET_SIZE == 4);
+ if (b->p[0] == value || b->p[1] == value ||
+ b->p[2] == value || b->p[3] == value)
+ return STB_TRUE;
+ return STB_FALSE;
+ }
+ if (code == STB_ps_array) {
+ stb_ps_array *a = GetArray(ps);
+ for (i=0; i < a->count; ++i)
+ if (a->p[i] == value)
+ return STB_TRUE;
+ return STB_FALSE;
+ } else {
+ stb_ps_hash *h = GetHash(ps);
+ stb_uint32 hash = stb_hashptr(value);
+ stb_uint32 s, n = hash & h->mask;
+ void **t = h->table;
+ if (t[n] == value) return STB_TRUE;
+ if (t[n] == NULL) return STB_FALSE;
+ s = stb_rehash(hash) | 1;
+ do {
+ n = (n + s) & h->mask;
+ if (t[n] == value) return STB_TRUE;
+ } while (t[n] != NULL);
+ return STB_FALSE;
+ }
+}
+
+stb_ps * stb_ps_add (stb_ps *ps, void *value)
+{
+ #ifdef STB_DEBUG
+ assert(!stb_ps_find(ps,value));
+ #endif
+ if (value == NULL) return ps; // ignore NULL adds to avoid bad breakage
+ assert((3 & (int)(size_t) value) == STB_ps_direct);
+ assert(stb_ps_fastlist_valid(value));
+ assert(value != STB_DEL); // STB_DEL is less likely
+
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ if (ps == NULL) return (stb_ps *) value;
+ return EncodeBucket(stb_bucket_create2(ps,value));
+
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ stb_ps_array *a;
+ assert(STB_BUCKET_SIZE == 4);
+ if (b->p[0] == NULL) { b->p[0] = value; return ps; }
+ if (b->p[1] == NULL) { b->p[1] = value; return ps; }
+ if (b->p[2] == NULL) { b->p[2] = value; return ps; }
+ if (b->p[3] == NULL) { b->p[3] = value; return ps; }
+ a = (stb_ps_array *) malloc(sizeof(*a) + 7 * sizeof(a->p[0])); // 8 slots, must be 2^k
+ memcpy(a->p, b, sizeof(*b));
+ a->p[4] = value;
+ a->count = 5;
+ stb_bucket_free(b);
+ return EncodeArray(a);
+ }
+
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ if (a->count == stb_ps_array_max) {
+ // promote from array to hash
+ stb_ps_hash *h = stb_ps_makehash(2 << stb_log2_ceil(a->count), a->count, a->p);
+ free(a);
+ return stb_ps_add(EncodeHash(h), value);
+ }
+ // do we need to resize the array? the array doubles in size when it
+ // crosses a power-of-two
+ if ((a->count & (a->count-1))==0) {
+ int newsize = a->count*2;
+ // clamp newsize to max if:
+ // 1. it's larger than max
+ // 2. newsize*1.5 is larger than max (to avoid extra resizing)
+ if (newsize + a->count > stb_ps_array_max)
+ newsize = stb_ps_array_max;
+ a = (stb_ps_array *) realloc(a, sizeof(*a) + (newsize-1) * sizeof(a->p[0]));
+ }
+ a->p[a->count++] = value;
+ return EncodeArray(a);
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ stb_uint32 hash = stb_hashptr(value);
+ stb_uint32 n = hash & h->mask;
+ void **t = h->table;
+ // find first NULL or STB_DEL entry
+ if (!stb_ps_empty((size_t)t[n])) {
+ stb_uint32 s = stb_rehash(hash) | 1;
+ do {
+ n = (n + s) & h->mask;
+ } while (!stb_ps_empty((size_t)t[n]));
+ }
+ if (t[n] == STB_DEL)
+ -- h->count_deletes;
+ t[n] = value;
+ ++ h->count;
+ if (h->count == h->grow_threshhold) {
+ stb_ps_hash *h2 = stb_ps_makehash(h->size*2, h->size, t);
+ free(h);
+ return EncodeHash(h2);
+ }
+ if (h->count + h->count_deletes == h->rehash_threshhold) {
+ stb_ps_hash *h2 = stb_ps_makehash(h->size, h->size, t);
+ free(h);
+ return EncodeHash(h2);
+ }
+ return ps;
+ }
+ }
+ return NULL; /* NOTREACHED */
+}
+
+stb_ps *stb_ps_remove(stb_ps *ps, void *value)
+{
+ #ifdef STB_DEBUG
+ assert(stb_ps_find(ps, value));
+ #endif
+ assert((3 & (int)(size_t) value) == STB_ps_direct);
+ if (value == NULL) return ps; // ignore NULL removes to avoid bad breakage
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ return ps == value ? NULL : ps;
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ int count=0;
+ assert(STB_BUCKET_SIZE == 4);
+ if (b->p[0] == value) b->p[0] = NULL; else count += (b->p[0] != NULL);
+ if (b->p[1] == value) b->p[1] = NULL; else count += (b->p[1] != NULL);
+ if (b->p[2] == value) b->p[2] = NULL; else count += (b->p[2] != NULL);
+ if (b->p[3] == value) b->p[3] = NULL; else count += (b->p[3] != NULL);
+ if (count == 1) { // shrink bucket at size 1
+ value = b->p[0];
+ if (value == NULL) value = b->p[1];
+ if (value == NULL) value = b->p[2];
+ if (value == NULL) value = b->p[3];
+ assert(value != NULL);
+ stb_bucket_free(b);
+ return (stb_ps *) value; // return STB_ps_direct of value
+ }
+ return ps;
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ int i;
+ for (i=0; i < a->count; ++i) {
+ if (a->p[i] == value) {
+ a->p[i] = a->p[--a->count];
+ if (a->count == 3) { // shrink to bucket!
+ stb_ps_bucket *b = stb_bucket_create3(a->p);
+ free(a);
+ return EncodeBucket(b);
+ }
+ return ps;
+ }
+ }
+ return ps;
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ stb_uint32 hash = stb_hashptr(value);
+ stb_uint32 s, n = hash & h->mask;
+ void **t = h->table;
+ if (t[n] != value) {
+ s = stb_rehash(hash) | 1;
+ do {
+ n = (n + s) & h->mask;
+ } while (t[n] != value);
+ }
+ t[n] = STB_DEL;
+ -- h->count;
+ ++ h->count_deletes;
+ // should we shrink down to an array?
+ if (h->count < stb_ps_array_max) {
+ int n = 1 << stb_log2_floor(stb_ps_array_max);
+ if (h->count < n) {
+ stb_ps_array *a = (stb_ps_array *) malloc(sizeof(*a) + (n-1) * sizeof(a->p[0]));
+ int i,j=0;
+ for (i=0; i < h->size; ++i)
+ if (!stb_ps_empty((size_t)t[i]))
+ a->p[j++] = t[i];
+ assert(j == h->count);
+ a->count = j;
+ free(h);
+ return EncodeArray(a);
+ }
+ }
+ if (h->count == h->shrink_threshhold) {
+ stb_ps_hash *h2 = stb_ps_makehash(h->size >> 1, h->size, t);
+ free(h);
+ return EncodeHash(h2);
+ }
+ return ps;
+ }
+ }
+ return ps; /* NOTREACHED */
+}
+
+stb_ps *stb_ps_remove_any(stb_ps *ps, void **value)
+{
+ assert(ps != NULL);
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ *value = ps;
+ return NULL;
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ int count=0, slast=0, last=0;
+ assert(STB_BUCKET_SIZE == 4);
+ if (b->p[0]) { ++count; last = 0; }
+ if (b->p[1]) { ++count; slast = last; last = 1; }
+ if (b->p[2]) { ++count; slast = last; last = 2; }
+ if (b->p[3]) { ++count; slast = last; last = 3; }
+ *value = b->p[last];
+ b->p[last] = 0;
+ if (count == 2) {
+ void *leftover = b->p[slast]; // second to last
+ stb_bucket_free(b);
+ return (stb_ps *) leftover;
+ }
+ return ps;
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ *value = a->p[a->count-1];
+ if (a->count == 4)
+ return stb_ps_remove(ps, *value);
+ --a->count;
+ return ps;
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ void **t = h->table;
+ stb_uint32 n = h->any_offset;
+ while (stb_ps_empty((size_t)t[n]))
+ n = (n + 1) & h->mask;
+ *value = t[n];
+ h->any_offset = (n+1) & h->mask;
+ // check if we need to skip down to the previous type
+ if (h->count-1 < stb_ps_array_max || h->count-1 == h->shrink_threshhold)
+ return stb_ps_remove(ps, *value);
+ t[n] = STB_DEL;
+ -- h->count;
+ ++ h->count_deletes;
+ return ps;
+ }
+ }
+ return ps; /* NOTREACHED */
+}
+
+
+void ** stb_ps_getlist(stb_ps *ps, int *count)
+{
+ int i,n=0;
+ void **p = NULL;
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ if (ps == NULL) { *count = 0; return NULL; }
+ p = (void **) malloc(sizeof(*p) * 1);
+ p[0] = ps;
+ *count = 1;
+ return p;
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ p = (void **) malloc(sizeof(*p) * STB_BUCKET_SIZE);
+ for (i=0; i < STB_BUCKET_SIZE; ++i)
+ if (b->p[i] != NULL)
+ p[n++] = b->p[i];
+ break;
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ p = (void **) malloc(sizeof(*p) * a->count);
+ memcpy(p, a->p, sizeof(*p) * a->count);
+ *count = a->count;
+ return p;
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ p = (void **) malloc(sizeof(*p) * h->count);
+ for (i=0; i < h->size; ++i)
+ if (!stb_ps_empty((size_t)h->table[i]))
+ p[n++] = h->table[i];
+ break;
+ }
+ }
+ *count = n;
+ return p;
+}
+
+int stb_ps_writelist(stb_ps *ps, void **list, int size )
+{
+ int i,n=0;
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ if (ps == NULL || size <= 0) return 0;
+ list[0] = ps;
+ return 1;
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ for (i=0; i < STB_BUCKET_SIZE; ++i)
+ if (b->p[i] != NULL && n < size)
+ list[n++] = b->p[i];
+ return n;
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ n = stb_min(size, a->count);
+ memcpy(list, a->p, sizeof(*list) * n);
+ return n;
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ if (size <= 0) return 0;
+ for (i=0; i < h->count; ++i) {
+ if (!stb_ps_empty((size_t)h->table[i])) {
+ list[n++] = h->table[i];
+ if (n == size) break;
+ }
+ }
+ return n;
+ }
+ }
+ return 0; /* NOTREACHED */
+}
+
+int stb_ps_enum(stb_ps *ps, void *data, int (*func)(void *value, void *data))
+{
+ int i;
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ if (ps == NULL) return STB_TRUE;
+ return func(ps, data);
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ for (i=0; i < STB_BUCKET_SIZE; ++i)
+ if (b->p[i] != NULL)
+ if (!func(b->p[i], data))
+ return STB_FALSE;
+ return STB_TRUE;
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ for (i=0; i < a->count; ++i)
+ if (!func(a->p[i], data))
+ return STB_FALSE;
+ return STB_TRUE;
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ for (i=0; i < h->count; ++i)
+ if (!stb_ps_empty((size_t)h->table[i]))
+ if (!func(h->table[i], data))
+ return STB_FALSE;
+ return STB_TRUE;
+ }
+ }
+ return STB_TRUE; /* NOTREACHED */
+}
+
+int stb_ps_count (stb_ps *ps)
+{
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ return ps != NULL;
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ return (b->p[0] != NULL) + (b->p[1] != NULL) +
+ (b->p[2] != NULL) + (b->p[3] != NULL);
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ return a->count;
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ return h->count;
+ }
+ }
+ return 0;
+}
+
+void ** stb_ps_fastlist(stb_ps *ps, int *count)
+{
+ static void *storage;
+
+ switch (3 & (int)(size_t) ps) {
+ case STB_ps_direct:
+ if (ps == NULL) { *count = 0; return NULL; }
+ storage = ps;
+ *count = 1;
+ return &storage;
+ case STB_ps_bucket: {
+ stb_ps_bucket *b = GetBucket(ps);
+ *count = STB_BUCKET_SIZE;
+ return b->p;
+ }
+ case STB_ps_array: {
+ stb_ps_array *a = GetArray(ps);
+ *count = a->count;
+ return a->p;
+ }
+ case STB_ps_hash: {
+ stb_ps_hash *h = GetHash(ps);
+ *count = h->size;
+ return h->table;
+ }
+ }
+ return NULL; /* NOTREACHED */
+}
+
+int stb_ps_subset(stb_ps *bigger, stb_ps *smaller)
+{
+ int i, listlen;
+ void **list = stb_ps_fastlist(smaller, &listlen);
+ for(i=0; i < listlen; ++i)
+ if (stb_ps_fastlist_valid(list[i]))
+ if (!stb_ps_find(bigger, list[i]))
+ return 0;
+ return 1;
+}
+
+int stb_ps_eq(stb_ps *p0, stb_ps *p1)
+{
+ if (stb_ps_count(p0) != stb_ps_count(p1))
+ return 0;
+ return stb_ps_subset(p0, p1);
+}
+
+#undef GetBucket
+#undef GetArray
+#undef GetHash
+
+#undef EncodeBucket
+#undef EncodeArray
+#undef EncodeHash
+
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Random Numbers via Meresenne Twister or LCG
+//
+
+STB_EXTERN unsigned int stb_srandLCG(unsigned int seed);
+STB_EXTERN unsigned int stb_randLCG(void);
+STB_EXTERN double stb_frandLCG(void);
+
+STB_EXTERN void stb_srand(unsigned int seed);
+STB_EXTERN unsigned int stb_rand(void);
+STB_EXTERN double stb_frand(void);
+STB_EXTERN void stb_shuffle(void *p, size_t n, size_t sz,
+ unsigned int seed);
+STB_EXTERN void stb_reverse(void *p, size_t n, size_t sz);
+
+STB_EXTERN unsigned int stb_randLCG_explicit(unsigned int seed);
+
+#define stb_rand_define(x,y) \
+ \
+ unsigned int x(void) \
+ { \
+ static unsigned int stb__rand = y; \
+ stb__rand = stb__rand * 2147001325 + 715136305; /* BCPL */ \
+ return 0x31415926 ^ ((stb__rand >> 16) + (stb__rand << 16)); \
+ }
+
+#ifdef STB_DEFINE
+unsigned int stb_randLCG_explicit(unsigned int seed)
+{
+ return seed * 2147001325 + 715136305;
+}
+
+static unsigned int stb__rand_seed=0;
+
+unsigned int stb_srandLCG(unsigned int seed)
+{
+ unsigned int previous = stb__rand_seed;
+ stb__rand_seed = seed;
+ return previous;
+}
+
+unsigned int stb_randLCG(void)
+{
+ stb__rand_seed = stb__rand_seed * 2147001325 + 715136305; // BCPL generator
+ // shuffle non-random bits to the middle, and xor to decorrelate with seed
+ return 0x31415926 ^ ((stb__rand_seed >> 16) + (stb__rand_seed << 16));
+}
+
+double stb_frandLCG(void)
+{
+ return stb_randLCG() / ((double) (1 << 16) * (1 << 16));
+}
+
+void stb_shuffle(void *p, size_t n, size_t sz, unsigned int seed)
+{
+ char *a;
+ unsigned int old_seed;
+ int i;
+ if (seed)
+ old_seed = stb_srandLCG(seed);
+ a = (char *) p + (n-1) * sz;
+
+ for (i=(int) n; i > 1; --i) {
+ int j = stb_randLCG() % i;
+ stb_swap(a, (char *) p + j * sz, sz);
+ a -= sz;
+ }
+ if (seed)
+ stb_srandLCG(old_seed);
+}
+
+void stb_reverse(void *p, size_t n, size_t sz)
+{
+ size_t i,j = n-1;
+ for (i=0; i < j; ++i,--j) {
+ stb_swap((char *) p + i * sz, (char *) p + j * sz, sz);
+ }
+}
+
+// public domain Mersenne Twister by Michael Brundage
+#define STB__MT_LEN 624
+
+int stb__mt_index = STB__MT_LEN*sizeof(int)+1;
+unsigned int stb__mt_buffer[STB__MT_LEN];
+
+void stb_srand(unsigned int seed)
+{
+ int i;
+ unsigned int old = stb_srandLCG(seed);
+ for (i = 0; i < STB__MT_LEN; i++)
+ stb__mt_buffer[i] = stb_randLCG();
+ stb_srandLCG(old);
+ stb__mt_index = STB__MT_LEN*sizeof(unsigned int);
+}
+
+#define STB__MT_IA 397
+#define STB__MT_IB (STB__MT_LEN - STB__MT_IA)
+#define STB__UPPER_MASK 0x80000000
+#define STB__LOWER_MASK 0x7FFFFFFF
+#define STB__MATRIX_A 0x9908B0DF
+#define STB__TWIST(b,i,j) ((b)[i] & STB__UPPER_MASK) | ((b)[j] & STB__LOWER_MASK)
+#define STB__MAGIC(s) (((s)&1)*STB__MATRIX_A)
+
+unsigned int stb_rand()
+{
+ unsigned int * b = stb__mt_buffer;
+ int idx = stb__mt_index;
+ unsigned int s,r;
+ int i;
+
+ if (idx >= STB__MT_LEN*sizeof(unsigned int)) {
+ if (idx > STB__MT_LEN*sizeof(unsigned int))
+ stb_srand(0);
+ idx = 0;
+ i = 0;
+ for (; i < STB__MT_IB; i++) {
+ s = STB__TWIST(b, i, i+1);
+ b[i] = b[i + STB__MT_IA] ^ (s >> 1) ^ STB__MAGIC(s);
+ }
+ for (; i < STB__MT_LEN-1; i++) {
+ s = STB__TWIST(b, i, i+1);
+ b[i] = b[i - STB__MT_IB] ^ (s >> 1) ^ STB__MAGIC(s);
+ }
+
+ s = STB__TWIST(b, STB__MT_LEN-1, 0);
+ b[STB__MT_LEN-1] = b[STB__MT_IA-1] ^ (s >> 1) ^ STB__MAGIC(s);
+ }
+ stb__mt_index = idx + sizeof(unsigned int);
+
+ r = *(unsigned int *)((unsigned char *)b + idx);
+
+ r ^= (r >> 11);
+ r ^= (r << 7) & 0x9D2C5680;
+ r ^= (r << 15) & 0xEFC60000;
+ r ^= (r >> 18);
+
+ return r;
+}
+
+double stb_frand(void)
+{
+ return stb_rand() / ((double) (1 << 16) * (1 << 16));
+}
+
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_dupe
+//
+// stb_dupe is a duplicate-finding system for very, very large data
+// structures--large enough that sorting is too slow, but not so large
+// that we can't keep all the data in memory. using it works as follows:
+//
+// 1. create an stb_dupe:
+// provide a hash function
+// provide an equality function
+// provide an estimate for the size
+// optionally provide a comparison function
+//
+// 2. traverse your data, 'adding' pointers to the stb_dupe
+//
+// 3. finish and ask for duplicates
+//
+// the stb_dupe will discard its intermediate data and build
+// a collection of sorted lists of duplicates, with non-duplicate
+// entries omitted entirely
+//
+//
+// Implementation strategy:
+//
+// while collecting the N items, we keep a hash table of approximate
+// size sqrt(N). (if you tell use the N up front, the hash table is
+// just that size exactly)
+//
+// each entry in the hash table is just an stb__arr of pointers (no need
+// to use stb_ps, because we don't need to delete from these)
+//
+// for step 3, for each entry in the hash table, we apply stb_dupe to it
+// recursively. once the size gets small enough (or doesn't decrease
+// significantly), we switch to either using qsort() on the comparison
+// function, or else we just do the icky N^2 gather
+
+
+typedef struct stb_dupe stb_dupe;
+
+typedef int (*stb_compare_func)(void *a, void *b);
+typedef int (*stb_hash_func)(void *a, unsigned int seed);
+
+STB_EXTERN void stb_dupe_free(stb_dupe *sd);
+STB_EXTERN stb_dupe *stb_dupe_create(stb_hash_func hash,
+ stb_compare_func eq, int size, stb_compare_func ineq);
+STB_EXTERN void stb_dupe_add(stb_dupe *sd, void *item);
+STB_EXTERN void stb_dupe_finish(stb_dupe *sd);
+STB_EXTERN int stb_dupe_numsets(stb_dupe *sd);
+STB_EXTERN void **stb_dupe_set(stb_dupe *sd, int num);
+STB_EXTERN int stb_dupe_set_count(stb_dupe *sd, int num);
+
+struct stb_dupe
+{
+ void ***hash_table;
+ int hash_size;
+ int size_log2;
+ int population;
+
+ int hash_shift;
+ stb_hash_func hash;
+
+ stb_compare_func eq;
+ stb_compare_func ineq;
+
+ void ***dupes;
+};
+
+#ifdef STB_DEFINE
+
+int stb_dupe_numsets(stb_dupe *sd)
+{
+ assert(sd->hash_table == NULL);
+ return stb_arr_len(sd->dupes);
+}
+
+void **stb_dupe_set(stb_dupe *sd, int num)
+{
+ assert(sd->hash_table == NULL);
+ return sd->dupes[num];
+}
+
+int stb_dupe_set_count(stb_dupe *sd, int num)
+{
+ assert(sd->hash_table == NULL);
+ return stb_arr_len(sd->dupes[num]);
+}
+
+stb_dupe *stb_dupe_create(stb_hash_func hash, stb_compare_func eq, int size,
+ stb_compare_func ineq)
+{
+ int i, hsize;
+ stb_dupe *sd = (stb_dupe *) malloc(sizeof(*sd));
+
+ sd->size_log2 = 4;
+ hsize = 1 << sd->size_log2;
+ while (hsize * hsize < size) {
+ ++sd->size_log2;
+ hsize *= 2;
+ }
+
+ sd->hash = hash;
+ sd->eq = eq;
+ sd->ineq = ineq;
+ sd->hash_shift = 0;
+
+ sd->population = 0;
+ sd->hash_size = hsize;
+ sd->hash_table = (void ***) malloc(sizeof(*sd->hash_table) * hsize);
+ for (i=0; i < hsize; ++i)
+ sd->hash_table[i] = NULL;
+
+ sd->dupes = NULL;
+
+ return sd;
+}
+
+void stb_dupe_add(stb_dupe *sd, void *item)
+{
+ stb_uint32 hash = sd->hash(item, sd->hash_shift);
+ int z = hash & (sd->hash_size-1);
+ stb_arr_push(sd->hash_table[z], item);
+ ++sd->population;
+}
+
+void stb_dupe_free(stb_dupe *sd)
+{
+ int i;
+ for (i=0; i < stb_arr_len(sd->dupes); ++i)
+ if (sd->dupes[i])
+ stb_arr_free(sd->dupes[i]);
+ stb_arr_free(sd->dupes);
+ free(sd);
+}
+
+static stb_compare_func stb__compare;
+
+static int stb__dupe_compare(const void *a, const void *b)
+{
+ void *p = *(void **) a;
+ void *q = *(void **) b;
+
+ return stb__compare(p,q);
+}
+
+void stb_dupe_finish(stb_dupe *sd)
+{
+ int i,j,k;
+ assert(sd->dupes == NULL);
+ for (i=0; i < sd->hash_size; ++i) {
+ void ** list = sd->hash_table[i];
+ if (list != NULL) {
+ int n = stb_arr_len(list);
+ // @TODO: measure to find good numbers instead of just making them up!
+ int thresh = (sd->ineq ? 200 : 20);
+ // if n is large enough to be worth it, and n is smaller than
+ // before (so we can guarantee we'll use a smaller hash table);
+ // and there are enough hash bits left, assuming full 32-bit hash
+ if (n > thresh && n < (sd->population >> 3) && sd->hash_shift + sd->size_log2*2 < 32) {
+
+ // recursively process this row using stb_dupe, O(N log log N)
+
+ stb_dupe *d = stb_dupe_create(sd->hash, sd->eq, n, sd->ineq);
+ d->hash_shift = stb_randLCG_explicit(sd->hash_shift);
+ for (j=0; j < n; ++j)
+ stb_dupe_add(d, list[j]);
+ stb_arr_free(sd->hash_table[i]);
+ stb_dupe_finish(d);
+ for (j=0; j < stb_arr_len(d->dupes); ++j) {
+ stb_arr_push(sd->dupes, d->dupes[j]);
+ d->dupes[j] = NULL; // take over ownership
+ }
+ stb_dupe_free(d);
+
+ } else if (sd->ineq) {
+
+ // process this row using qsort(), O(N log N)
+ stb__compare = sd->ineq;
+ qsort(list, n, sizeof(list[0]), stb__dupe_compare);
+
+ // find equal subsequences of the list
+ for (j=0; j < n-1; ) {
+ // find a subsequence from j..k
+ for (k=j; k < n; ++k)
+ // only use ineq so eq can be left undefined
+ if (sd->ineq(list[j], list[k]))
+ break;
+ // k is the first one not in the subsequence
+ if (k-j > 1) {
+ void **mylist = NULL;
+ stb_arr_setlen(mylist, k-j);
+ memcpy(mylist, list+j, sizeof(list[j]) * (k-j));
+ stb_arr_push(sd->dupes, mylist);
+ }
+ j = k;
+ }
+ stb_arr_free(sd->hash_table[i]);
+ } else {
+
+ // process this row using eq(), O(N^2)
+ for (j=0; j < n; ++j) {
+ if (list[j] != NULL) {
+ void **output = NULL;
+ for (k=j+1; k < n; ++k) {
+ if (sd->eq(list[j], list[k])) {
+ if (output == NULL)
+ stb_arr_push(output, list[j]);
+ stb_arr_push(output, list[k]);
+ list[k] = NULL;
+ }
+ }
+ list[j] = NULL;
+ if (output)
+ stb_arr_push(sd->dupes, output);
+ }
+ }
+ stb_arr_free(sd->hash_table[i]);
+ }
+ }
+ }
+ free(sd->hash_table);
+ sd->hash_table = NULL;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// templatized Sort routine
+//
+// This is an attempt to implement a templated sorting algorithm.
+// To use it, you have to explicitly instantiate it as a _function_,
+// then you call that function. This allows the comparison to be inlined,
+// giving the sort similar performance to C++ sorts.
+//
+// It implements quicksort with three-way-median partitioning (generally
+// well-behaved), with a final insertion sort pass.
+//
+// When you define the compare expression, you should assume you have
+// elements of your array pointed to by 'a' and 'b', and perform the comparison
+// on those. OR you can use one or more statements; first say '0;', then
+// write whatever code you want, and compute the result into a variable 'c'.
+
+#define stb_declare_sort(FUNCNAME, TYPE) \
+ void FUNCNAME(TYPE *p, int n)
+#define stb_define_sort(FUNCNAME,TYPE,COMPARE) \
+ stb__define_sort( void, FUNCNAME,TYPE,COMPARE)
+#define stb_define_sort_static(FUNCNAME,TYPE,COMPARE) \
+ stb__define_sort(static void, FUNCNAME,TYPE,COMPARE)
+
+#define stb__define_sort(MODE, FUNCNAME, TYPE, COMPARE) \
+ \
+static void STB_(FUNCNAME,_ins_sort)(TYPE *p, int n) \
+{ \
+ int i,j; \
+ for (i=1; i < n; ++i) { \
+ TYPE t = p[i], *a = &t; \
+ j = i; \
+ while (j > 0) { \
+ TYPE *b = &p[j-1]; \
+ int c = COMPARE; \
+ if (!c) break; \
+ p[j] = p[j-1]; \
+ --j; \
+ } \
+ if (i != j) \
+ p[j] = t; \
+ } \
+} \
+ \
+static void STB_(FUNCNAME,_quicksort)(TYPE *p, int n) \
+{ \
+ /* threshold for transitioning to insertion sort */ \
+ while (n > 12) { \
+ TYPE *a,*b,t; \
+ int c01,c12,c,m,i,j; \
+ \
+ /* compute median of three */ \
+ m = n >> 1; \
+ a = &p[0]; \
+ b = &p[m]; \
+ c = COMPARE; \
+ c01 = c; \
+ a = &p[m]; \
+ b = &p[n-1]; \
+ c = COMPARE; \
+ c12 = c; \
+ /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ \
+ if (c01 != c12) { \
+ /* otherwise, we'll need to swap something else to middle */ \
+ int z; \
+ a = &p[0]; \
+ b = &p[n-1]; \
+ c = COMPARE; \
+ /* 0>mid && mid<n: 0>n => n; 0<n => 0 */ \
+ /* 0<mid && mid>n: 0>n => 0; 0<n => n */ \
+ z = (c == c12) ? 0 : n-1; \
+ t = p[z]; \
+ p[z] = p[m]; \
+ p[m] = t; \
+ } \
+ /* now p[m] is the median-of-three */ \
+ /* swap it to the beginning so it won't move around */ \
+ t = p[0]; \
+ p[0] = p[m]; \
+ p[m] = t; \
+ \
+ /* partition loop */ \
+ i=1; \
+ j=n-1; \
+ for(;;) { \
+ /* handling of equality is crucial here */ \
+ /* for sentinels & efficiency with duplicates */ \
+ b = &p[0]; \
+ for (;;++i) { \
+ a=&p[i]; \
+ c = COMPARE; \
+ if (!c) break; \
+ } \
+ a = &p[0]; \
+ for (;;--j) { \
+ b=&p[j]; \
+ c = COMPARE; \
+ if (!c) break; \
+ } \
+ /* make sure we haven't crossed */ \
+ if (i >= j) break; \
+ t = p[i]; \
+ p[i] = p[j]; \
+ p[j] = t; \
+ \
+ ++i; \
+ --j; \
+ } \
+ /* recurse on smaller side, iterate on larger */ \
+ if (j < (n-i)) { \
+ STB_(FUNCNAME,_quicksort)(p,j); \
+ p = p+i; \
+ n = n-i; \
+ } else { \
+ STB_(FUNCNAME,_quicksort)(p+i, n-i); \
+ n = j; \
+ } \
+ } \
+} \
+ \
+MODE FUNCNAME(TYPE *p, int n) \
+{ \
+ STB_(FUNCNAME, _quicksort)(p, n); \
+ STB_(FUNCNAME, _ins_sort)(p, n); \
+} \
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_bitset an array of booleans indexed by integers
+//
+
+typedef stb_uint32 stb_bitset;
+
+STB_EXTERN stb_bitset *stb_bitset_new(int value, int len);
+
+#define stb_bitset_clearall(arr,len) (memset(arr, 0, 4 * (len)))
+#define stb_bitset_setall(arr,len) (memset(arr, 255, 4 * (len)))
+
+#define stb_bitset_setbit(arr,n) ((arr)[(n) >> 5] |= (1 << (n & 31)))
+#define stb_bitset_clearbit(arr,n) ((arr)[(n) >> 5] &= ~(1 << (n & 31)))
+#define stb_bitset_testbit(arr,n) ((arr)[(n) >> 5] & (1 << (n & 31)))
+
+STB_EXTERN stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len);
+
+STB_EXTERN int *stb_bitset_getlist(stb_bitset *out, int start, int end);
+
+STB_EXTERN int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len);
+STB_EXTERN int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len);
+STB_EXTERN int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len);
+STB_EXTERN int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len);
+STB_EXTERN int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len);
+
+#ifdef STB_DEFINE
+int stb_bitset_eq(stb_bitset *p0, stb_bitset *p1, int len)
+{
+ int i;
+ for (i=0; i < len; ++i)
+ if (p0[i] != p1[i]) return 0;
+ return 1;
+}
+
+int stb_bitset_disjoint(stb_bitset *p0, stb_bitset *p1, int len)
+{
+ int i;
+ for (i=0; i < len; ++i)
+ if (p0[i] & p1[i]) return 0;
+ return 1;
+}
+
+int stb_bitset_disjoint_0(stb_bitset *p0, stb_bitset *p1, int len)
+{
+ int i;
+ for (i=0; i < len; ++i)
+ if ((p0[i] | p1[i]) != 0xffffffff) return 0;
+ return 1;
+}
+
+int stb_bitset_subset(stb_bitset *bigger, stb_bitset *smaller, int len)
+{
+ int i;
+ for (i=0; i < len; ++i)
+ if ((bigger[i] & smaller[i]) != smaller[i]) return 0;
+ return 1;
+}
+
+stb_bitset *stb_bitset_union(stb_bitset *p0, stb_bitset *p1, int len)
+{
+ int i;
+ stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len);
+ for (i=0; i < len; ++i) d[i] = p0[i] | p1[i];
+ return d;
+}
+
+int stb_bitset_unioneq_changed(stb_bitset *p0, stb_bitset *p1, int len)
+{
+ int i, changed=0;
+ for (i=0; i < len; ++i) {
+ stb_bitset d = p0[i] | p1[i];
+ if (d != p0[i]) {
+ p0[i] = d;
+ changed = 1;
+ }
+ }
+ return changed;
+}
+
+stb_bitset *stb_bitset_new(int value, int len)
+{
+ int i;
+ stb_bitset *d = (stb_bitset *) malloc(sizeof(*d) * len);
+ if (value) value = 0xffffffff;
+ for (i=0; i < len; ++i) d[i] = value;
+ return d;
+}
+
+int *stb_bitset_getlist(stb_bitset *out, int start, int end)
+{
+ int *list = NULL;
+ int i;
+ for (i=start; i < end; ++i)
+ if (stb_bitset_testbit(out, i))
+ stb_arr_push(list, i);
+ return list;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_wordwrap quality word-wrapping for fixed-width fonts
+//
+
+STB_EXTERN int stb_wordwrap(int *pairs, int pair_max, int count, char *str);
+STB_EXTERN int *stb_wordwrapalloc(int count, char *str);
+
+#ifdef STB_DEFINE
+
+int stb_wordwrap(int *pairs, int pair_max, int count, char *str)
+{
+ int n=0,i=0, start=0,nonwhite=0;
+ if (pairs == NULL) pair_max = 0x7ffffff0;
+ else pair_max *= 2;
+ // parse
+ for(;;) {
+ int s=i; // first whitespace char; last nonwhite+1
+ int w; // word start
+ // accept whitespace
+ while (isspace(str[i])) {
+ if (str[i] == '\n' || str[i] == '\r') {
+ if (str[i] + str[i+1] == '\n' + '\r') ++i;
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = s-start;
+ n += 2;
+ nonwhite=0;
+ start = i+1;
+ s = start;
+ }
+ ++i;
+ }
+ if (i >= start+count) {
+ // we've gone off the end using whitespace
+ if (nonwhite) {
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = s-start;
+ n += 2;
+ start = s = i;
+ nonwhite=0;
+ } else {
+ // output all the whitespace
+ while (i >= start+count) {
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = count;
+ n += 2;
+ start += count;
+ }
+ s = start;
+ }
+ }
+
+ if (str[i] == 0) break;
+ // now scan out a word and see if it fits
+ w = i;
+ while (str[i] && !isspace(str[i])) {
+ ++i;
+ }
+ // wrapped?
+ if (i > start + count) {
+ // huge?
+ if (i-s <= count) {
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = s-start;
+ n += 2;
+ start = w;
+ } else {
+ // This word is longer than one line. If we wrap it onto N lines
+ // there are leftover chars. do those chars fit on the cur line?
+ // But if we have leading whitespace, we force it to start here.
+ if ((w-start) + ((i-w) % count) <= count || !nonwhite) {
+ // output a full line
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = count;
+ n += 2;
+ start += count;
+ w = start;
+ } else {
+ // output a partial line, trimming trailing whitespace
+ if (s != start) {
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = s-start;
+ n += 2;
+ start = w;
+ }
+ }
+ // now output full lines as needed
+ while (start + count <= i) {
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = count;
+ n += 2;
+ start += count;
+ }
+ }
+ }
+ nonwhite=1;
+ }
+ if (start < i) {
+ if (n >= pair_max) return -1;
+ if (pairs) pairs[n] = start, pairs[n+1] = i-start;
+ n += 2;
+ }
+ return n>>1;
+}
+
+int *stb_wordwrapalloc(int count, char *str)
+{
+ int n = stb_wordwrap(NULL,0,count,str);
+ int *z = NULL;
+ stb_arr_setlen(z, n*2);
+ stb_wordwrap(z, n, count, str);
+ return z;
+}
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// stb_match: wildcards and regexping
+//
+
+STB_EXTERN int stb_wildmatch (char *expr, char *candidate);
+STB_EXTERN int stb_wildmatchi(char *expr, char *candidate);
+STB_EXTERN int stb_wildfind (char *expr, char *candidate);
+STB_EXTERN int stb_wildfindi (char *expr, char *candidate);
+
+STB_EXTERN int stb_regex(char *regex, char *candidate);
+
+typedef struct stb_matcher stb_matcher;
+
+STB_EXTERN stb_matcher *stb_regex_matcher(char *regex);
+STB_EXTERN int stb_matcher_match(stb_matcher *m, char *str);
+STB_EXTERN int stb_matcher_find(stb_matcher *m, char *str);
+STB_EXTERN void stb_matcher_free(stb_matcher *f);
+
+STB_EXTERN stb_matcher *stb_lex_matcher(void);
+STB_EXTERN int stb_lex_item(stb_matcher *m, const char *str, int result);
+STB_EXTERN int stb_lex_item_wild(stb_matcher *matcher, const char *regex, int result);
+STB_EXTERN int stb_lex(stb_matcher *m, char *str, int *len);
+
+
+
+#ifdef STB_DEFINE
+
+static int stb__match_qstring(char *candidate, char *qstring, int qlen, int insensitive)
+{
+ int i;
+ if (insensitive) {
+ for (i=0; i < qlen; ++i)
+ if (qstring[i] == '?') {
+ if (!candidate[i]) return 0;
+ } else
+ if (tolower(qstring[i]) != tolower(candidate[i]))
+ return 0;
+ } else {
+ for (i=0; i < qlen; ++i)
+ if (qstring[i] == '?') {
+ if (!candidate[i]) return 0;
+ } else
+ if (qstring[i] != candidate[i])
+ return 0;
+ }
+ return 1;
+}
+
+static int stb__find_qstring(char *candidate, char *qstring, int qlen, int insensitive)
+{
+ char c;
+
+ int offset=0;
+ while (*qstring == '?') {
+ ++qstring;
+ --qlen;
+ ++candidate;
+ if (qlen == 0) return 0;
+ if (*candidate == 0) return -1;
+ }
+
+ c = *qstring++;
+ --qlen;
+ if (insensitive) c = tolower(c);
+
+ while (candidate[offset]) {
+ if (c == (insensitive ? tolower(candidate[offset]) : candidate[offset]))
+ if (stb__match_qstring(candidate+offset+1, qstring, qlen, insensitive))
+ return offset;
+ ++offset;
+ }
+
+ return -1;
+}
+
+int stb__wildmatch_raw2(char *expr, char *candidate, int search, int insensitive)
+{
+ int where=0;
+ int start = -1;
+
+ if (!search) {
+ // parse to first '*'
+ if (*expr != '*')
+ start = 0;
+ while (*expr != '*') {
+ if (!*expr)
+ return *candidate == 0 ? 0 : -1;
+ if (*expr == '?') {
+ if (!*candidate) return -1;
+ } else {
+ if (insensitive) {
+ if (tolower(*candidate) != tolower(*expr))
+ return -1;
+ } else
+ if (*candidate != *expr)
+ return -1;
+ }
+ ++candidate, ++expr, ++where;
+ }
+ } else {
+ // 0-length search string
+ if (!*expr)
+ return 0;
+ }
+
+ assert(search || *expr == '*');
+ if (!search)
+ ++expr;
+
+ // implicit '*' at this point
+
+ while (*expr) {
+ int o=0;
+ // combine redundant * characters
+ while (expr[0] == '*') ++expr;
+
+ // ok, at this point, expr[-1] == '*',
+ // and expr[0] != '*'
+
+ if (!expr[0]) return start >= 0 ? start : 0;
+
+ // now find next '*'
+ o = 0;
+ while (expr[o] != '*') {
+ if (expr[o] == 0)
+ break;
+ ++o;
+ }
+ // if no '*', scan to end, then match at end
+ if (expr[o] == 0 && !search) {
+ int z;
+ for (z=0; z < o; ++z)
+ if (candidate[z] == 0)
+ return -1;
+ while (candidate[z])
+ ++z;
+ // ok, now check if they match
+ if (stb__match_qstring(candidate+z-o, expr, o, insensitive))
+ return start >= 0 ? start : 0;
+ return -1;
+ } else {
+ // if yes '*', then do stb__find_qmatch on the intervening chars
+ int n = stb__find_qstring(candidate, expr, o, insensitive);
+ if (n < 0)
+ return -1;
+ if (start < 0)
+ start = where + n;
+ expr += o;
+ candidate += n+o;
+ }
+
+ if (*expr == 0) {
+ assert(search);
+ return start;
+ }
+
+ assert(*expr == '*');
+ ++expr;
+ }
+
+ return start >= 0 ? start : 0;
+}
+
+int stb__wildmatch_raw(char *expr, char *candidate, int search, int insensitive)
+{
+ char buffer[256];
+ // handle multiple search strings
+ char *s = strchr(expr, ';');
+ char *last = expr;
+ while (s) {
+ int z;
+ // need to allow for non-writeable strings... assume they're small
+ if (s - last < 256) {
+ stb_strncpy(buffer, last, (int) (s-last+1));
+ z = stb__wildmatch_raw2(buffer, candidate, search, insensitive);
+ } else {
+ *s = 0;
+ z = stb__wildmatch_raw2(last, candidate, search, insensitive);
+ *s = ';';
+ }
+ if (z >= 0) return z;
+ last = s+1;
+ s = strchr(last, ';');
+ }
+ return stb__wildmatch_raw2(last, candidate, search, insensitive);
+}
+
+int stb_wildmatch(char *expr, char *candidate)
+{
+ return stb__wildmatch_raw(expr, candidate, 0,0) >= 0;
+}
+
+int stb_wildmatchi(char *expr, char *candidate)
+{
+ return stb__wildmatch_raw(expr, candidate, 0,1) >= 0;
+}
+
+int stb_wildfind(char *expr, char *candidate)
+{
+ return stb__wildmatch_raw(expr, candidate, 1,0);
+}
+
+int stb_wildfindi(char *expr, char *candidate)
+{
+ return stb__wildmatch_raw(expr, candidate, 1,1);
+}
+
+typedef struct
+{
+ stb_int16 transition[256];
+} stb_dfa;
+
+// an NFA node represents a state you're in; it then has
+// an arbitrary number of edges dangling off of it
+// note this isn't utf8-y
+typedef struct
+{
+ stb_int16 match; // character/set to match
+ stb_uint16 node; // output node to go to
+} stb_nfa_edge;
+
+typedef struct
+{
+ stb_int16 goal; // does reaching this win the prize?
+ stb_uint8 active; // is this in the active list
+ stb_nfa_edge *out;
+ stb_uint16 *eps; // list of epsilon closures
+} stb_nfa_node;
+
+#define STB__DFA_UNDEF -1
+#define STB__DFA_GOAL -2
+#define STB__DFA_END -3
+#define STB__DFA_MGOAL -4
+#define STB__DFA_VALID 0
+
+#define STB__NFA_STOP_GOAL -1
+
+// compiled regexp
+struct stb_matcher
+{
+ stb_uint16 start_node;
+ stb_int16 dfa_start;
+ stb_uint32 *charset;
+ int num_charset;
+ int match_start;
+ stb_nfa_node *nodes;
+ int does_lex;
+
+ // dfa matcher
+ stb_dfa * dfa;
+ stb_uint32 * dfa_mapping;
+ stb_int16 * dfa_result;
+ int num_words_per_dfa;
+};
+
+static int stb__add_node(stb_matcher *matcher)
+{
+ stb_nfa_node z;
+ z.active = 0;
+ z.eps = 0;
+ z.goal = 0;
+ z.out = 0;
+ stb_arr_push(matcher->nodes, z);
+ return stb_arr_len(matcher->nodes)-1;
+}
+
+static void stb__add_epsilon(stb_matcher *matcher, int from, int to)
+{
+ assert(from != to);
+ if (matcher->nodes[from].eps == NULL)
+ stb_arr_malloc((void **) &matcher->nodes[from].eps, matcher);
+ stb_arr_push(matcher->nodes[from].eps, to);
+}
+
+static void stb__add_edge(stb_matcher *matcher, int from, int to, int type)
+{
+ stb_nfa_edge z = { (stb_int16)type, (stb_uint16)to };
+ if (matcher->nodes[from].out == NULL)
+ stb_arr_malloc((void **) &matcher->nodes[from].out, matcher);
+ stb_arr_push(matcher->nodes[from].out, z);
+}
+
+static char *stb__reg_parse_alt(stb_matcher *m, int s, char *r, stb_uint16 *e);
+static char *stb__reg_parse(stb_matcher *matcher, int start, char *regex, stb_uint16 *end)
+{
+ int n;
+ int last_start = -1;
+ stb_uint16 last_end = start;
+
+ while (*regex) {
+ switch (*regex) {
+ case '(':
+ last_start = last_end;
+ regex = stb__reg_parse_alt(matcher, last_end, regex+1, &last_end);
+ if (regex == NULL || *regex != ')')
+ return NULL;
+ ++regex;
+ break;
+
+ case '|':
+ case ')':
+ *end = last_end;
+ return regex;
+
+ case '?':
+ if (last_start < 0) return NULL;
+ stb__add_epsilon(matcher, last_start, last_end);
+ ++regex;
+ break;
+
+ case '*':
+ if (last_start < 0) return NULL;
+ stb__add_epsilon(matcher, last_start, last_end);
+
+ // fall through
+
+ case '+':
+ if (last_start < 0) return NULL;
+ stb__add_epsilon(matcher, last_end, last_start);
+ // prevent links back to last_end from chaining to last_start
+ n = stb__add_node(matcher);
+ stb__add_epsilon(matcher, last_end, n);
+ last_end = n;
+ ++regex;
+ break;
+
+ case '{': // not supported!
+ // @TODO: given {n,m}, clone last_start to last_end m times,
+ // and include epsilons from start to first m-n blocks
+ return NULL;
+
+ case '\\':
+ ++regex;
+ if (!*regex) return NULL;
+
+ // fallthrough
+ default: // match exactly this character
+ n = stb__add_node(matcher);
+ stb__add_edge(matcher, last_end, n, *regex);
+ last_start = last_end;
+ last_end = n;
+ ++regex;
+ break;
+
+ case '$':
+ n = stb__add_node(matcher);
+ stb__add_edge(matcher, last_end, n, '\n');
+ last_start = last_end;
+ last_end = n;
+ ++regex;
+ break;
+
+ case '.':
+ n = stb__add_node(matcher);
+ stb__add_edge(matcher, last_end, n, -1);
+ last_start = last_end;
+ last_end = n;
+ ++regex;
+ break;
+
+ case '[': {
+ stb_uint8 flags[256];
+ int invert = 0,z;
+ ++regex;
+ if (matcher->num_charset == 0) {
+ matcher->charset = (stb_uint *) stb_malloc(matcher, sizeof(*matcher->charset) * 256);
+ memset(matcher->charset, 0, sizeof(*matcher->charset) * 256);
+ }
+
+ memset(flags,0,sizeof(flags));
+
+ // leading ^ is special
+ if (*regex == '^')
+ ++regex, invert = 1;
+
+ // leading ] is special
+ if (*regex == ']') {
+ flags[(int) ']'] = 1;
+ ++regex;
+ }
+ while (*regex != ']') {
+ stb_uint a;
+ if (!*regex) return NULL;
+ a = *regex++;
+ if (regex[0] == '-' && regex[1] != ']') {
+ stb_uint i,b = regex[1];
+ regex += 2;
+ if (b == 0) return NULL;
+ if (a > b) return NULL;
+ for (i=a; i <= b; ++i)
+ flags[i] = 1;
+ } else
+ flags[a] = 1;
+ }
+ ++regex;
+ if (invert) {
+ int i;
+ for (i=0; i < 256; ++i)
+ flags[i] = 1-flags[i];
+ }
+
+ // now check if any existing charset matches
+ for (z=0; z < matcher->num_charset; ++z) {
+ int i, k[2] = { 0, 1 << z};
+ for (i=0; i < 256; ++i) {
+ unsigned int f = k[flags[i]];
+ if ((matcher->charset[i] & k[1]) != f)
+ break;
+ }
+ if (i == 256) break;
+ }
+
+ if (z == matcher->num_charset) {
+ int i;
+ ++matcher->num_charset;
+ if (matcher->num_charset > 32) {
+ assert(0); /* NOTREACHED */
+ return NULL; // too many charsets, oops
+ }
+ for (i=0; i < 256; ++i)
+ if (flags[i])
+ matcher->charset[i] |= (1 << z);
+ }
+
+ n = stb__add_node(matcher);
+ stb__add_edge(matcher, last_end, n, -2 - z);
+ last_start = last_end;
+ last_end = n;
+ break;
+ }
+ }
+ }
+ *end = last_end;
+ return regex;
+}
+
+static char *stb__reg_parse_alt(stb_matcher *matcher, int start, char *regex, stb_uint16 *end)
+{
+ stb_uint16 last_end = start;
+ stb_uint16 main_end;
+
+ int head, tail;
+
+ head = stb__add_node(matcher);
+ stb__add_epsilon(matcher, start, head);
+
+ regex = stb__reg_parse(matcher, head, regex, &last_end);
+ if (regex == NULL) return NULL;
+ if (*regex == 0 || *regex == ')') {
+ *end = last_end;
+ return regex;
+ }
+
+ main_end = last_end;
+ tail = stb__add_node(matcher);
+
+ stb__add_epsilon(matcher, last_end, tail);
+
+ // start alternatives from the same starting node; use epsilon
+ // transitions to combine their endings
+ while(*regex && *regex != ')') {
+ assert(*regex == '|');
+ head = stb__add_node(matcher);
+ stb__add_epsilon(matcher, start, head);
+ regex = stb__reg_parse(matcher, head, regex+1, &last_end);
+ if (regex == NULL)
+ return NULL;
+ stb__add_epsilon(matcher, last_end, tail);
+ }
+
+ *end = tail;
+ return regex;
+}
+
+static char *stb__wild_parse(stb_matcher *matcher, int start, char *str, stb_uint16 *end)
+{
+ int n;
+ stb_uint16 last_end;
+
+ last_end = stb__add_node(matcher);
+ stb__add_epsilon(matcher, start, last_end);
+
+ while (*str) {
+ switch (*str) {
+ // fallthrough
+ default: // match exactly this character
+ n = stb__add_node(matcher);
+ if (toupper(*str) == tolower(*str)) {
+ stb__add_edge(matcher, last_end, n, *str);
+ } else {
+ stb__add_edge(matcher, last_end, n, tolower(*str));
+ stb__add_edge(matcher, last_end, n, toupper(*str));
+ }
+ last_end = n;
+ ++str;
+ break;
+
+ case '?':
+ n = stb__add_node(matcher);
+ stb__add_edge(matcher, last_end, n, -1);
+ last_end = n;
+ ++str;
+ break;
+
+ case '*':
+ n = stb__add_node(matcher);
+ stb__add_edge(matcher, last_end, n, -1);
+ stb__add_epsilon(matcher, last_end, n);
+ stb__add_epsilon(matcher, n, last_end);
+ last_end = n;
+ ++str;
+ break;
+ }
+ }
+
+ // now require end of string to match
+ n = stb__add_node(matcher);
+ stb__add_edge(matcher, last_end, n, 0);
+ last_end = n;
+
+ *end = last_end;
+ return str;
+}
+
+static int stb__opt(stb_matcher *m, int n)
+{
+ for(;;) {
+ stb_nfa_node *p = &m->nodes[n];
+ if (p->goal) return n;
+ if (stb_arr_len(p->out)) return n;
+ if (stb_arr_len(p->eps) != 1) return n;
+ n = p->eps[0];
+ }
+}
+
+static void stb__optimize(stb_matcher *m)
+{
+ // if the target of any edge is a node with exactly
+ // one out-epsilon, shorten it
+ int i,j;
+ for (i=0; i < stb_arr_len(m->nodes); ++i) {
+ stb_nfa_node *p = &m->nodes[i];
+ for (j=0; j < stb_arr_len(p->out); ++j)
+ p->out[j].node = stb__opt(m,p->out[j].node);
+ for (j=0; j < stb_arr_len(p->eps); ++j)
+ p->eps[j] = stb__opt(m,p->eps[j] );
+ }
+ m->start_node = stb__opt(m,m->start_node);
+}
+
+void stb_matcher_free(stb_matcher *f)
+{
+ stb_free(f);
+}
+
+static stb_matcher *stb__alloc_matcher(void)
+{
+ stb_matcher *matcher = (stb_matcher *) stb_malloc(0,sizeof(*matcher));
+
+ matcher->start_node = 0;
+ stb_arr_malloc((void **) &matcher->nodes, matcher);
+ matcher->num_charset = 0;
+ matcher->match_start = 0;
+ matcher->does_lex = 0;
+
+ matcher->dfa_start = STB__DFA_UNDEF;
+ stb_arr_malloc((void **) &matcher->dfa, matcher);
+ stb_arr_malloc((void **) &matcher->dfa_mapping, matcher);
+ stb_arr_malloc((void **) &matcher->dfa_result, matcher);
+
+ stb__add_node(matcher);
+
+ return matcher;
+}
+
+static void stb__lex_reset(stb_matcher *matcher)
+{
+ // flush cached dfa data
+ stb_arr_setlen(matcher->dfa, 0);
+ stb_arr_setlen(matcher->dfa_mapping, 0);
+ stb_arr_setlen(matcher->dfa_result, 0);
+ matcher->dfa_start = STB__DFA_UNDEF;
+}
+
+stb_matcher *stb_regex_matcher(char *regex)
+{
+ char *z;
+ stb_uint16 end;
+ stb_matcher *matcher = stb__alloc_matcher();
+ if (*regex == '^') {
+ matcher->match_start = 1;
+ ++regex;
+ }
+
+ z = stb__reg_parse_alt(matcher, matcher->start_node, regex, &end);
+
+ if (!z || *z) {
+ stb_free(matcher);
+ return NULL;
+ }
+
+ ((matcher->nodes)[(int) end]).goal = STB__NFA_STOP_GOAL;
+
+ return matcher;
+}
+
+stb_matcher *stb_lex_matcher(void)
+{
+ stb_matcher *matcher = stb__alloc_matcher();
+
+ matcher->match_start = 1;
+ matcher->does_lex = 1;
+
+ return matcher;
+}
+
+int stb_lex_item(stb_matcher *matcher, const char *regex, int result)
+{
+ char *z;
+ stb_uint16 end;
+
+ z = stb__reg_parse_alt(matcher, matcher->start_node, (char*) regex, &end);
+
+ if (z == NULL)
+ return 0;
+
+ stb__lex_reset(matcher);
+
+ matcher->nodes[(int) end].goal = result;
+ return 1;
+}
+
+int stb_lex_item_wild(stb_matcher *matcher, const char *regex, int result)
+{
+ char *z;
+ stb_uint16 end;
+
+ z = stb__wild_parse(matcher, matcher->start_node, (char*) regex, &end);
+
+ if (z == NULL)
+ return 0;
+
+ stb__lex_reset(matcher);
+
+ matcher->nodes[(int) end].goal = result;
+ return 1;
+}
+
+static void stb__clear(stb_matcher *m, stb_uint16 *list)
+{
+ int i;
+ for (i=0; i < stb_arr_len(list); ++i)
+ m->nodes[(int) list[i]].active = 0;
+}
+
+static int stb__clear_goalcheck(stb_matcher *m, stb_uint16 *list)
+{
+ int i, t=0;
+ for (i=0; i < stb_arr_len(list); ++i) {
+ t += m->nodes[(int) list[i]].goal;
+ m->nodes[(int) list[i]].active = 0;
+ }
+ return t;
+}
+
+static stb_uint16 * stb__add_if_inactive(stb_matcher *m, stb_uint16 *list, int n)
+{
+ if (!m->nodes[n].active) {
+ stb_arr_push(list, n);
+ m->nodes[n].active = 1;
+ }
+ return list;
+}
+
+static stb_uint16 * stb__eps_closure(stb_matcher *m, stb_uint16 *list)
+{
+ int i,n = stb_arr_len(list);
+
+ for(i=0; i < n; ++i) {
+ stb_uint16 *e = m->nodes[(int) list[i]].eps;
+ if (e) {
+ int j,k = stb_arr_len(e);
+ for (j=0; j < k; ++j)
+ list = stb__add_if_inactive(m, list, e[j]);
+ n = stb_arr_len(list);
+ }
+ }
+
+ return list;
+}
+
+int stb_matcher_match(stb_matcher *m, char *str)
+{
+ int result = 0;
+ int i,j,y,z;
+ stb_uint16 *previous = NULL;
+ stb_uint16 *current = NULL;
+ stb_uint16 *temp;
+
+ stb_arr_setsize(previous, 4);
+ stb_arr_setsize(current, 4);
+
+ previous = stb__add_if_inactive(m, previous, m->start_node);
+ previous = stb__eps_closure(m,previous);
+ stb__clear(m, previous);
+
+ while (*str && stb_arr_len(previous)) {
+ y = stb_arr_len(previous);
+ for (i=0; i < y; ++i) {
+ stb_nfa_node *n = &m->nodes[(int) previous[i]];
+ z = stb_arr_len(n->out);
+ for (j=0; j < z; ++j) {
+ if (n->out[j].match >= 0) {
+ if (n->out[j].match == *str)
+ current = stb__add_if_inactive(m, current, n->out[j].node);
+ } else if (n->out[j].match == -1) {
+ if (*str != '\n')
+ current = stb__add_if_inactive(m, current, n->out[j].node);
+ } else if (n->out[j].match < -1) {
+ int z = -n->out[j].match - 2;
+ if (m->charset[(stb_uint8) *str] & (1 << z))
+ current = stb__add_if_inactive(m, current, n->out[j].node);
+ }
+ }
+ }
+ stb_arr_setlen(previous, 0);
+
+ temp = previous;
+ previous = current;
+ current = temp;
+
+ previous = stb__eps_closure(m,previous);
+ stb__clear(m, previous);
+
+ ++str;
+ }
+
+ // transition to pick up a '$' at the end
+ y = stb_arr_len(previous);
+ for (i=0; i < y; ++i)
+ m->nodes[(int) previous[i]].active = 1;
+
+ for (i=0; i < y; ++i) {
+ stb_nfa_node *n = &m->nodes[(int) previous[i]];
+ z = stb_arr_len(n->out);
+ for (j=0; j < z; ++j) {
+ if (n->out[j].match == '\n')
+ current = stb__add_if_inactive(m, current, n->out[j].node);
+ }
+ }
+
+ previous = stb__eps_closure(m,previous);
+ stb__clear(m, previous);
+
+ y = stb_arr_len(previous);
+ for (i=0; i < y; ++i)
+ if (m->nodes[(int) previous[i]].goal)
+ result = 1;
+
+ stb_arr_free(previous);
+ stb_arr_free(current);
+
+ return result && *str == 0;
+}
+
+stb_int16 stb__get_dfa_node(stb_matcher *m, stb_uint16 *list)
+{
+ stb_uint16 node;
+ stb_uint32 data[8], *state, *newstate;
+ int i,j,n;
+
+ state = (stb_uint32 *) stb_temp(data, m->num_words_per_dfa * 4);
+ memset(state, 0, m->num_words_per_dfa*4);
+
+ n = stb_arr_len(list);
+ for (i=0; i < n; ++i) {
+ int x = list[i];
+ state[x >> 5] |= 1 << (x & 31);
+ }
+
+ // @TODO use a hash table
+ n = stb_arr_len(m->dfa_mapping);
+ i=j=0;
+ for(; j < n; ++i, j += m->num_words_per_dfa) {
+ // @TODO special case for <= 32
+ if (!memcmp(state, m->dfa_mapping + j, m->num_words_per_dfa*4)) {
+ node = i;
+ goto done;
+ }
+ }
+
+ assert(stb_arr_len(m->dfa) == i);
+ node = i;
+
+ newstate = stb_arr_addn(m->dfa_mapping, m->num_words_per_dfa);
+ memcpy(newstate, state, m->num_words_per_dfa*4);
+
+ // set all transitions to 'unknown'
+ stb_arr_add(m->dfa);
+ memset(m->dfa[i].transition, -1, sizeof(m->dfa[i].transition));
+
+ if (m->does_lex) {
+ int result = -1;
+ n = stb_arr_len(list);
+ for (i=0; i < n; ++i) {
+ if (m->nodes[(int) list[i]].goal > result)
+ result = m->nodes[(int) list[i]].goal;
+ }
+
+ stb_arr_push(m->dfa_result, result);
+ }
+
+done:
+ stb_tempfree(data, state);
+ return node;
+}
+
+static int stb__matcher_dfa(stb_matcher *m, char *str_c, int *len)
+{
+ stb_uint8 *str = (stb_uint8 *) str_c;
+ stb_int16 node,prevnode;
+ stb_dfa *trans;
+ int match_length = 0;
+ stb_int16 match_result=0;
+
+ if (m->dfa_start == STB__DFA_UNDEF) {
+ stb_uint16 *list;
+
+ m->num_words_per_dfa = (stb_arr_len(m->nodes)+31) >> 5;
+ stb__optimize(m);
+
+ list = stb__add_if_inactive(m, NULL, m->start_node);
+ list = stb__eps_closure(m,list);
+ if (m->does_lex) {
+ m->dfa_start = stb__get_dfa_node(m,list);
+ stb__clear(m, list);
+ // DON'T allow start state to be a goal state!
+ // this allows people to specify regexes that can match 0
+ // characters without them actually matching (also we don't
+ // check _before_ advancing anyway
+ if (m->dfa_start <= STB__DFA_MGOAL)
+ m->dfa_start = -(m->dfa_start - STB__DFA_MGOAL);
+ } else {
+ if (stb__clear_goalcheck(m, list))
+ m->dfa_start = STB__DFA_GOAL;
+ else
+ m->dfa_start = stb__get_dfa_node(m,list);
+ }
+ stb_arr_free(list);
+ }
+
+ prevnode = STB__DFA_UNDEF;
+ node = m->dfa_start;
+ trans = m->dfa;
+
+ if (m->dfa_start == STB__DFA_GOAL)
+ return 1;
+
+ for(;;) {
+ assert(node >= STB__DFA_VALID);
+
+ // fast inner DFA loop; especially if STB__DFA_VALID is 0
+
+ do {
+ prevnode = node;
+ node = trans[node].transition[*str++];
+ } while (node >= STB__DFA_VALID);
+
+ assert(node >= STB__DFA_MGOAL - stb_arr_len(m->dfa));
+ assert(node < stb_arr_len(m->dfa));
+
+ // special case for lex: need _longest_ match, so notice goal
+ // state without stopping
+ if (node <= STB__DFA_MGOAL) {
+ match_length = (int) (str - (stb_uint8 *) str_c);
+ node = -(node - STB__DFA_MGOAL);
+ match_result = node;
+ continue;
+ }
+
+ // slow NFA->DFA conversion
+
+ // or we hit the goal or the end of the string, but those
+ // can only happen once per search...
+
+ if (node == STB__DFA_UNDEF) {
+ // build a list -- @TODO special case <= 32 states
+ // heck, use a more compact data structure for <= 16 and <= 8 ?!
+
+ // @TODO keep states/newstates around instead of reallocating them
+ stb_uint16 *states = NULL;
+ stb_uint16 *newstates = NULL;
+ int i,j,y,z;
+ stb_uint32 *flags = &m->dfa_mapping[prevnode * m->num_words_per_dfa];
+ assert(prevnode != STB__DFA_UNDEF);
+ stb_arr_setsize(states, 4);
+ stb_arr_setsize(newstates,4);
+ for (j=0; j < m->num_words_per_dfa; ++j) {
+ for (i=0; i < 32; ++i) {
+ if (*flags & (1 << i))
+ stb_arr_push(states, j*32+i);
+ }
+ ++flags;
+ }
+ // states is now the states we were in in the previous node;
+ // so now we can compute what node it transitions to on str[-1]
+
+ y = stb_arr_len(states);
+ for (i=0; i < y; ++i) {
+ stb_nfa_node *n = &m->nodes[(int) states[i]];
+ z = stb_arr_len(n->out);
+ for (j=0; j < z; ++j) {
+ if (n->out[j].match >= 0) {
+ if (n->out[j].match == str[-1] || (str[-1] == 0 && n->out[j].match == '\n'))
+ newstates = stb__add_if_inactive(m, newstates, n->out[j].node);
+ } else if (n->out[j].match == -1) {
+ if (str[-1] != '\n' && str[-1])
+ newstates = stb__add_if_inactive(m, newstates, n->out[j].node);
+ } else if (n->out[j].match < -1) {
+ int z = -n->out[j].match - 2;
+ if (m->charset[str[-1]] & (1 << z))
+ newstates = stb__add_if_inactive(m, newstates, n->out[j].node);
+ }
+ }
+ }
+ // AND add in the start state!
+ if (!m->match_start || (str[-1] == '\n' && !m->does_lex))
+ newstates = stb__add_if_inactive(m, newstates, m->start_node);
+ // AND epsilon close it
+ newstates = stb__eps_closure(m, newstates);
+ // if it's a goal state, then that's all there is to it
+ if (stb__clear_goalcheck(m, newstates)) {
+ if (m->does_lex) {
+ match_length = (int) (str - (stb_uint8 *) str_c);
+ node = stb__get_dfa_node(m,newstates);
+ match_result = node;
+ node = -node + STB__DFA_MGOAL;
+ trans = m->dfa; // could have gotten realloc()ed
+ } else
+ node = STB__DFA_GOAL;
+ } else if (str[-1] == 0 || stb_arr_len(newstates) == 0) {
+ node = STB__DFA_END;
+ } else {
+ node = stb__get_dfa_node(m,newstates);
+ trans = m->dfa; // could have gotten realloc()ed
+ }
+ trans[prevnode].transition[str[-1]] = node;
+ if (node <= STB__DFA_MGOAL)
+ node = -(node - STB__DFA_MGOAL);
+ stb_arr_free(newstates);
+ stb_arr_free(states);
+ }
+
+ if (node == STB__DFA_GOAL) {
+ return 1;
+ }
+ if (node == STB__DFA_END) {
+ if (m->does_lex) {
+ if (match_result) {
+ if (len) *len = match_length;
+ return m->dfa_result[(int) match_result];
+ }
+ }
+ return 0;
+ }
+
+ assert(node != STB__DFA_UNDEF);
+ }
+}
+
+int stb_matcher_find(stb_matcher *m, char *str)
+{
+ assert(m->does_lex == 0);
+ return stb__matcher_dfa(m, str, NULL);
+}
+
+int stb_lex(stb_matcher *m, char *str, int *len)
+{
+ assert(m->does_lex);
+ return stb__matcher_dfa(m, str, len);
+}
+
+int stb_regex(char *regex, char *str)
+{
+ static stb_perfect p;
+ static stb_matcher ** matchers;
+ static char ** regexps;
+ static char ** regexp_cache;
+ static unsigned short *mapping;
+ int z = stb_perfect_hash(&p, (int)(size_t) regex);
+ if (z >= 0) {
+ if (strcmp(regex, regexp_cache[(int) mapping[z]])) {
+ int i = mapping[z];
+ stb_matcher_free(matchers[i]);
+ free(regexp_cache[i]);
+ regexps[i] = regex;
+ regexp_cache[i] = stb_p_strdup(regex);
+ matchers[i] = stb_regex_matcher(regex);
+ }
+ } else {
+ int i,n;
+ if (regex == NULL) {
+ for (i=0; i < stb_arr_len(matchers); ++i) {
+ stb_matcher_free(matchers[i]);
+ free(regexp_cache[i]);
+ }
+ stb_arr_free(matchers);
+ stb_arr_free(regexps);
+ stb_arr_free(regexp_cache);
+ stb_perfect_destroy(&p);
+ free(mapping); mapping = NULL;
+ return -1;
+ }
+ stb_arr_push(regexps, regex);
+ stb_arr_push(regexp_cache, stb_p_strdup(regex));
+ stb_arr_push(matchers, stb_regex_matcher(regex));
+ stb_perfect_destroy(&p);
+ n = stb_perfect_create(&p, (unsigned int *) (char **) regexps, stb_arr_len(regexps));
+ mapping = (unsigned short *) realloc(mapping, n * sizeof(*mapping));
+ for (i=0; i < stb_arr_len(regexps); ++i)
+ mapping[stb_perfect_hash(&p, (int)(size_t) regexps[i])] = i;
+ z = stb_perfect_hash(&p, (int)(size_t) regex);
+ }
+ return stb_matcher_find(matchers[(int) mapping[z]], str);
+}
+
+#endif // STB_DEFINE
+
+
+#if 0
+//////////////////////////////////////////////////////////////////////////////
+//
+// C source-code introspection
+//
+
+// runtime structure
+typedef struct
+{
+ char *name;
+ char *type; // base type
+ char *comment; // content of comment field
+ int size; // size of base type
+ int offset; // field offset
+ int arrcount[8]; // array sizes; -1 = pointer indirection; 0 = end of list
+} stb_info_field;
+
+typedef struct
+{
+ char *structname;
+ int size;
+ int num_fields;
+ stb_info_field *fields;
+} stb_info_struct;
+
+extern stb_info_struct stb_introspect_output[];
+
+//
+
+STB_EXTERN void stb_introspect_precompiled(stb_info_struct *compiled);
+STB_EXTERN void stb__introspect(char *path, char *file);
+
+#define stb_introspect_ship() stb__introspect(NULL, NULL, stb__introspect_output)
+
+#ifdef STB_SHIP
+#define stb_introspect() stb_introspect_ship()
+#define stb_introspect_path(p) stb_introspect_ship()
+#else
+// bootstrapping: define stb_introspect() (or 'path') the first time
+#define stb_introspect() stb__introspect(NULL, __FILE__, NULL)
+#define stb_introspect_auto() stb__introspect(NULL, __FILE__, stb__introspect_output)
+
+#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL)
+#define stb_introspect_path(p) stb__introspect(p, __FILE__, NULL)
+#endif
+
+#ifdef STB_DEFINE
+
+#ifndef STB_INTROSPECT_CPP
+ #ifdef __cplusplus
+ #define STB_INTROSPECT_CPP 1
+ #else
+ #define STB_INTROSPECT_CPP 0
+ #endif
+#endif
+
+void stb_introspect_precompiled(stb_info_struct *compiled)
+{
+
+}
+
+
+static void stb__introspect_filename(char *buffer, char *path)
+{
+ #if STB_INTROSPECT_CPP
+ stb_p_sprintf(buffer stb_p_size(9999), "%s/stb_introspect.cpp", path);
+ #else
+ stb_p_sprintf(buffer stb_p_size(9999), "%s/stb_introspect.c", path);
+ #endif
+}
+
+static void stb__introspect_compute(char *path, char *file)
+{
+ int i;
+ char ** include_list = NULL;
+ char ** introspect_list = NULL;
+ FILE *f;
+ f = stb_p_fopen(file, "w");
+ if (!f) return;
+
+ fputs("// if you get compiler errors, change the following 0 to a 1:\n", f);
+ fputs("#define STB_INTROSPECT_INVALID 0\n\n", f);
+ fputs("// this will force the code to compile, and force the introspector\n", f);
+ fputs("// to run and then exit, allowing you to recompile\n\n\n", f);
+ fputs("#include \"stb.h\"\n\n",f );
+ fputs("#if STB_INTROSPECT_INVALID\n", f);
+ fputs(" stb_info_struct stb__introspect_output[] = { (void *) 1 }\n", f);
+ fputs("#else\n\n", f);
+ for (i=0; i < stb_arr_len(include_list); ++i)
+ fprintf(f, " #include \"%s\"\n", include_list[i]);
+
+ fputs(" stb_info_struct stb__introspect_output[] =\n{\n", f);
+ for (i=0; i < stb_arr_len(introspect_list); ++i)
+ fprintf(f, " stb_introspect_%s,\n", introspect_list[i]);
+ fputs(" };\n", f);
+ fputs("#endif\n", f);
+ fclose(f);
+}
+
+static stb_info_struct *stb__introspect_info;
+
+#ifndef STB_SHIP
+
+#endif
+
+void stb__introspect(char *path, char *file, stb_info_struct *compiled)
+{
+ static int first=1;
+ if (!first) return;
+ first=0;
+
+ stb__introspect_info = compiled;
+
+ #ifndef STB_SHIP
+ if (path || file) {
+ int bail_flag = compiled && compiled[0].structname == (void *) 1;
+ int needs_building = bail_flag;
+ struct stb__stat st;
+ char buffer[1024], buffer2[1024];
+ if (!path) {
+ stb_splitpath(buffer, file, STB_PATH);
+ path = buffer;
+ }
+ // bail if the source path doesn't exist
+ if (!stb_fexists(path)) return;
+
+ stb__introspect_filename(buffer2, path);
+
+ // get source/include files timestamps, compare to output-file timestamp;
+ // if mismatched, regenerate
+
+ if (stb__stat(buffer2, &st))
+ needs_building = STB_TRUE;
+
+ {
+ // find any file that contains an introspection command and is newer
+ // if needs_building is already true, we don't need to do this test,
+ // but we still need these arrays, so go ahead and get them
+ char **all[3];
+ all[0] = stb_readdir_files_mask(path, "*.h");
+ all[1] = stb_readdir_files_mask(path, "*.c");
+ all[2] = stb_readdir_files_mask(path, "*.cpp");
+ int i,j;
+ if (needs_building) {
+ for (j=0; j < 3; ++j) {
+ for (i=0; i < stb_arr_len(all[j]); ++i) {
+ struct stb__stat st2;
+ if (!stb__stat(all[j][i], &st2)) {
+ if (st.st_mtime < st2.st_mtime) {
+ char *z = stb_filec(all[j][i], NULL);
+ int found=STB_FALSE;
+ while (y) {
+ y = strstr(y, "//si");
+ if (y && isspace(y[4])) {
+ found = STB_TRUE;
+ break;
+ }
+ }
+ needs_building = STB_TRUE;
+ goto done;
+ }
+ }
+ }
+ }
+ done:;
+ }
+ char *z = stb_filec(all[i], NULL), *y = z;
+ int found=STB_FALSE;
+ while (y) {
+ y = strstr(y, "//si");
+ if (y && isspace(y[4])) {
+ found = STB_TRUE;
+ break;
+ }
+ }
+ if (found)
+ stb_arr_push(introspect_h, stb_p_strdup(all[i]));
+ free(z);
+ }
+ }
+ stb_readdir_free(all);
+ if (!needs_building) {
+ for (i=0; i < stb_arr_len(introspect_h); ++i) {
+ struct stb__stat st2;
+ if (!stb__stat(introspect_h[i], &st2))
+ if (st.st_mtime < st2.st_mtime)
+ needs_building = STB_TRUE;
+ }
+ }
+
+ if (needs_building) {
+ stb__introspect_compute(path, buffer2);
+ }
+ }
+ }
+ #endif
+}
+#endif
+#endif
+
+#ifdef STB_INTROSPECT
+// compile-time code-generator
+#define INTROSPECT(x) int main(int argc, char **argv) { stb__introspect(__FILE__); return 0; }
+#define FILE(x)
+
+void stb__introspect(char *filename)
+{
+ char *file = stb_file(filename, NULL);
+ char *s = file, *t, **p;
+ char *out_name = "stb_introspect.c";
+ char *out_path;
+ STB_ARR(char) filelist = NULL;
+ int i,n;
+ if (!file) stb_fatal("Couldn't open %s", filename);
+
+ out_path = stb_splitpathdup(filename, STB_PATH);
+
+ // search for the macros
+ while (*s) {
+ char buffer[256];
+ while (*s && !isupper(*s)) ++s;
+ s = stb_strtok_invert(buffer, s, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+ s = stb_skipwhite(s);
+ if (*s == '(') {
+ ++s;
+ t = strchr(s, ')');
+ if (t == NULL) stb_fatal("Error parsing %s", filename);
+
+ }
+ }
+}
+
+
+
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// STB-C sliding-window dictionary compression
+//
+// This uses a DEFLATE-style sliding window, but no bitwise entropy.
+// Everything is on byte boundaries, so you could then apply a byte-wise
+// entropy code, though that's nowhere near as effective.
+//
+// An STB-C stream begins with a 16-byte header:
+// 4 bytes: 0x57 0xBC 0x00 0x00
+// 8 bytes: big-endian size of decompressed data, 64-bits
+// 4 bytes: big-endian size of window (how far back decompressor may need)
+//
+// The following symbols appear in the stream (these were determined ad hoc,
+// not by analysis):
+//
+// [dict] 00000100 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx
+// [END] 00000101 11111010 cccccccc cccccccc cccccccc cccccccc
+// [dict] 00000110 yyyyyyyy yyyyyyyy yyyyyyyy xxxxxxxx
+// [literals] 00000111 zzzzzzzz zzzzzzzz
+// [literals] 00001zzz zzzzzzzz
+// [dict] 00010yyy yyyyyyyy yyyyyyyy xxxxxxxx xxxxxxxx
+// [dict] 00011yyy yyyyyyyy yyyyyyyy xxxxxxxx
+// [literals] 001zzzzz
+// [dict] 01yyyyyy yyyyyyyy xxxxxxxx
+// [dict] 1xxxxxxx yyyyyyyy
+//
+// xxxxxxxx: match length - 1
+// yyyyyyyy: backwards distance - 1
+// zzzzzzzz: num literals - 1
+// cccccccc: adler32 checksum of decompressed data
+// (all big-endian)
+
+
+STB_EXTERN stb_uint stb_decompress_length(stb_uchar *input);
+STB_EXTERN stb_uint stb_decompress(stb_uchar *out,stb_uchar *in,stb_uint len);
+STB_EXTERN stb_uint stb_compress (stb_uchar *out,stb_uchar *in,stb_uint len);
+STB_EXTERN void stb_compress_window(int z);
+STB_EXTERN void stb_compress_hashsize(unsigned int z);
+
+STB_EXTERN int stb_compress_tofile(char *filename, char *in, stb_uint len);
+STB_EXTERN int stb_compress_intofile(FILE *f, char *input, stb_uint len);
+STB_EXTERN char *stb_decompress_fromfile(char *filename, stb_uint *len);
+
+STB_EXTERN int stb_compress_stream_start(FILE *f);
+STB_EXTERN void stb_compress_stream_end(int close);
+STB_EXTERN void stb_write(char *data, int data_len);
+
+#ifdef STB_DEFINE
+
+stb_uint stb_decompress_length(stb_uchar *input)
+{
+ return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11];
+}
+
+//////////////////// decompressor ///////////////////////
+
+// simple implementation that just writes whole thing into big block
+
+static unsigned char *stb__barrier;
+static unsigned char *stb__barrier2;
+static unsigned char *stb__barrier3;
+static unsigned char *stb__barrier4;
+
+static stb_uchar *stb__dout;
+static void stb__match(stb_uchar *data, stb_uint length)
+{
+ // INVERSE of memmove... write each byte before copying the next...
+ assert (stb__dout + length <= stb__barrier);
+ if (stb__dout + length > stb__barrier) { stb__dout += length; return; }
+ if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; }
+ while (length--) *stb__dout++ = *data++;
+}
+
+static void stb__lit(stb_uchar *data, stb_uint length)
+{
+ assert (stb__dout + length <= stb__barrier);
+ if (stb__dout + length > stb__barrier) { stb__dout += length; return; }
+ if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; }
+ memcpy(stb__dout, data, length);
+ stb__dout += length;
+}
+
+#define stb__in2(x) ((i[x] << 8) + i[(x)+1])
+#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1))
+#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1))
+
+static stb_uchar *stb_decompress_token(stb_uchar *i)
+{
+ if (*i >= 0x20) { // use fewer if's for cases that expand small
+ if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2;
+ else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3;
+ else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1);
+ } else { // more ifs for cases that expand large, since overhead is amortized
+ if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4;
+ else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5;
+ else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1);
+ else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1);
+ else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5;
+ else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6;
+ }
+ return i;
+}
+
+stb_uint stb_decompress(stb_uchar *output, stb_uchar *i, stb_uint length)
+{
+ stb_uint olen;
+ if (stb__in4(0) != 0x57bC0000) return 0;
+ if (stb__in4(4) != 0) return 0; // error! stream is > 4GB
+ olen = stb_decompress_length(i);
+ stb__barrier2 = i;
+ stb__barrier3 = i+length;
+ stb__barrier = output + olen;
+ stb__barrier4 = output;
+ i += 16;
+
+ stb__dout = output;
+ while (1) {
+ stb_uchar *old_i = i;
+ i = stb_decompress_token(i);
+ if (i == old_i) {
+ if (*i == 0x05 && i[1] == 0xfa) {
+ assert(stb__dout == output + olen);
+ if (stb__dout != output + olen) return 0;
+ if (stb_adler32(1, output, olen) != (stb_uint) stb__in4(2))
+ return 0;
+ return olen;
+ } else {
+ assert(0); /* NOTREACHED */
+ return 0;
+ }
+ }
+ assert(stb__dout <= output + olen);
+ if (stb__dout > output + olen)
+ return 0;
+ }
+}
+
+char *stb_decompress_fromfile(char *filename, unsigned int *len)
+{
+ unsigned int n;
+ char *q;
+ unsigned char *p;
+ FILE *f = stb_p_fopen(filename, "rb"); if (f == NULL) return NULL;
+ fseek(f, 0, SEEK_END);
+ n = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ p = (unsigned char * ) malloc(n); if (p == NULL) return NULL;
+ fread(p, 1, n, f);
+ fclose(f);
+ if (p == NULL) return NULL;
+ if (p[0] != 0x57 || p[1] != 0xBc || p[2] || p[3]) { free(p); return NULL; }
+ q = (char *) malloc(stb_decompress_length(p)+1);
+ if (!q) { free(p); return NULL; }
+ *len = stb_decompress((unsigned char *) q, p, n);
+ if (*len) q[*len] = 0;
+ free(p);
+ return q;
+}
+
+#if 0
+// streaming decompressor
+
+static struct
+{
+ stb__uchar *in_buffer;
+ stb__uchar *match;
+
+ stb__uint pending_literals;
+ stb__uint pending_match;
+} xx;
+
+
+
+static void stb__match(stb_uchar *data, stb_uint length)
+{
+ // INVERSE of memmove... write each byte before copying the next...
+ assert (stb__dout + length <= stb__barrier);
+ if (stb__dout + length > stb__barrier) { stb__dout += length; return; }
+ if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; }
+ while (length--) *stb__dout++ = *data++;
+}
+
+static void stb__lit(stb_uchar *data, stb_uint length)
+{
+ assert (stb__dout + length <= stb__barrier);
+ if (stb__dout + length > stb__barrier) { stb__dout += length; return; }
+ if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; }
+ memcpy(stb__dout, data, length);
+ stb__dout += length;
+}
+
+static void sx_match(stb_uchar *data, stb_uint length)
+{
+ xx.match = data;
+ xx.pending_match = length;
+}
+
+static void sx_lit(stb_uchar *data, stb_uint length)
+{
+ xx.pending_lit = length;
+}
+
+static int stb_decompress_token_state(void)
+{
+ stb__uchar *i = xx.in_buffer;
+
+ if (*i >= 0x20) { // use fewer if's for cases that expand small
+ if (*i >= 0x80) sx_match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2;
+ else if (*i >= 0x40) sx_match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3;
+ else /* *i >= 0x20 */ sx_lit(i+1, i[0] - 0x20 + 1), i += 1;
+ } else { // more ifs for cases that expand large, since overhead is amortized
+ if (*i >= 0x18) sx_match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4;
+ else if (*i >= 0x10) sx_match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5;
+ else if (*i >= 0x08) sx_lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2;
+ else if (*i == 0x07) sx_lit(i+3, stb__in2(1) + 1), i += 3;
+ else if (*i == 0x06) sx_match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5;
+ else if (*i == 0x04) sx_match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6;
+ else return 0;
+ }
+ xx.in_buffer = i;
+ return 1;
+}
+#endif
+
+
+
+//////////////////// compressor ///////////////////////
+
+static unsigned int stb_matchlen(stb_uchar *m1, stb_uchar *m2, stb_uint maxlen)
+{
+ stb_uint i;
+ for (i=0; i < maxlen; ++i)
+ if (m1[i] != m2[i]) return i;
+ return i;
+}
+
+// simple implementation that just takes the source data in a big block
+
+static stb_uchar *stb__out;
+static FILE *stb__outfile;
+static stb_uint stb__outbytes;
+
+static void stb__write(unsigned char v)
+{
+ fputc(v, stb__outfile);
+ ++stb__outbytes;
+}
+
+#define stb_out(v) (stb__out ? (void)(*stb__out++ = (stb_uchar) (v)) : stb__write((stb_uchar) (v)))
+
+static void stb_out2(stb_uint v)
+{
+ stb_out(v >> 8);
+ stb_out(v);
+}
+
+static void stb_out3(stb_uint v) { stb_out(v >> 16); stb_out(v >> 8); stb_out(v); }
+static void stb_out4(stb_uint v) { stb_out(v >> 24); stb_out(v >> 16);
+ stb_out(v >> 8 ); stb_out(v); }
+
+static void outliterals(stb_uchar *in, ptrdiff_t numlit)
+{
+ while (numlit > 65536) {
+ outliterals(in,65536);
+ in += 65536;
+ numlit -= 65536;
+ }
+
+ if (numlit == 0) ;
+ else if (numlit <= 32) stb_out (0x000020 + (stb_uint) numlit-1);
+ else if (numlit <= 2048) stb_out2(0x000800 + (stb_uint) numlit-1);
+ else /* numlit <= 65536) */ stb_out3(0x070000 + (stb_uint) numlit-1);
+
+ if (stb__out) {
+ memcpy(stb__out,in,numlit);
+ stb__out += numlit;
+ } else
+ fwrite(in, 1, numlit, stb__outfile);
+}
+
+static int stb__window = 0x40000; // 256K
+void stb_compress_window(int z)
+{
+ if (z >= 0x1000000) z = 0x1000000; // limit of implementation
+ if (z < 0x100) z = 0x100; // insanely small
+ stb__window = z;
+}
+
+static int stb_not_crap(int best, int dist)
+{
+ return ((best > 2 && dist <= 0x00100)
+ || (best > 5 && dist <= 0x04000)
+ || (best > 7 && dist <= 0x80000));
+}
+
+static stb_uint stb__hashsize = 32768;
+void stb_compress_hashsize(unsigned int y)
+{
+ unsigned int z = 1024;
+ while (z < y) z <<= 1;
+ stb__hashsize = z >> 2; // pass in bytes, store #pointers
+}
+
+// note that you can play with the hashing functions all you
+// want without needing to change the decompressor
+#define stb__hc(q,h,c) (((h) << 7) + ((h) >> 25) + q[c])
+#define stb__hc2(q,h,c,d) (((h) << 14) + ((h) >> 18) + (q[c] << 7) + q[d])
+#define stb__hc3(q,c,d,e) ((q[c] << 14) + (q[d] << 7) + q[e])
+
+static stb_uint32 stb__running_adler;
+
+static int stb_compress_chunk(stb_uchar *history,
+ stb_uchar *start,
+ stb_uchar *end,
+ int length,
+ int *pending_literals,
+ stb_uchar **chash,
+ stb_uint mask)
+{
+ int window = stb__window;
+ stb_uint match_max;
+ stb_uchar *lit_start = start - *pending_literals;
+ stb_uchar *q = start;
+
+ #define STB__SCRAMBLE(h) (((h) + ((h) >> 16)) & mask)
+
+ // stop short of the end so we don't scan off the end doing
+ // the hashing; this means we won't compress the last few bytes
+ // unless they were part of something longer
+ while (q < start+length && q+12 < end) {
+ int m;
+ stb_uint h1,h2,h3,h4, h;
+ stb_uchar *t;
+ int best = 2, dist=0;
+
+ if (q+65536 > end)
+ match_max = (stb_uint) (end-q);
+ else
+ match_max = 65536u;
+
+ #define stb__nc(b,d) ((d) <= window && ((b) > 9 || stb_not_crap(b,d)))
+
+ #define STB__TRY(t,p) /* avoid retrying a match we already tried */ \
+ if (p ? dist != (int) (q-t) : 1) \
+ if ((m = (int) stb_matchlen(t, q, match_max)) > best)\
+ if (stb__nc(m,(int) (q-(t)))) \
+ best = m, dist = (int) (q - (t))
+
+ // rather than search for all matches, only try 4 candidate locations,
+ // chosen based on 4 different hash functions of different lengths.
+ // this strategy is inspired by LZO; hashing is unrolled here using the
+ // 'hc' macro
+ h = stb__hc3(q,0, 1, 2); h1 = STB__SCRAMBLE(h);
+ t = chash[h1]; if (t) STB__TRY(t,0);
+ h = stb__hc2(q,h, 3, 4); h2 = STB__SCRAMBLE(h);
+ h = stb__hc2(q,h, 5, 6); t = chash[h2]; if (t) STB__TRY(t,1);
+ h = stb__hc2(q,h, 7, 8); h3 = STB__SCRAMBLE(h);
+ h = stb__hc2(q,h, 9,10); t = chash[h3]; if (t) STB__TRY(t,1);
+ h = stb__hc2(q,h,11,12); h4 = STB__SCRAMBLE(h);
+ t = chash[h4]; if (t) STB__TRY(t,1);
+
+ // because we use a shared hash table, can only update it
+ // _after_ we've probed all of them
+ chash[h1] = chash[h2] = chash[h3] = chash[h4] = q;
+
+ if (best > 2)
+ assert(dist > 0);
+
+ // see if our best match qualifies
+ if (best < 3) { // fast path literals
+ ++q;
+ } else if (best > 2 && best <= 0x80 && dist <= 0x100) {
+ outliterals(lit_start, q-lit_start); lit_start = (q += best);
+ stb_out(0x80 + best-1);
+ stb_out(dist-1);
+ } else if (best > 5 && best <= 0x100 && dist <= 0x4000) {
+ outliterals(lit_start, q-lit_start); lit_start = (q += best);
+ stb_out2(0x4000 + dist-1);
+ stb_out(best-1);
+ } else if (best > 7 && best <= 0x100 && dist <= 0x80000) {
+ outliterals(lit_start, q-lit_start); lit_start = (q += best);
+ stb_out3(0x180000 + dist-1);
+ stb_out(best-1);
+ } else if (best > 8 && best <= 0x10000 && dist <= 0x80000) {
+ outliterals(lit_start, q-lit_start); lit_start = (q += best);
+ stb_out3(0x100000 + dist-1);
+ stb_out2(best-1);
+ } else if (best > 9 && dist <= 0x1000000) {
+ if (best > 65536) best = 65536;
+ outliterals(lit_start, q-lit_start); lit_start = (q += best);
+ if (best <= 0x100) {
+ stb_out(0x06);
+ stb_out3(dist-1);
+ stb_out(best-1);
+ } else {
+ stb_out(0x04);
+ stb_out3(dist-1);
+ stb_out2(best-1);
+ }
+ } else { // fallback literals if no match was a balanced tradeoff
+ ++q;
+ }
+ }
+
+ // if we didn't get all the way, add the rest to literals
+ if (q-start < length)
+ q = start+length;
+
+ // the literals are everything from lit_start to q
+ *pending_literals = (int) (q - lit_start);
+
+ stb__running_adler = stb_adler32(stb__running_adler, start, (int) (q - start));
+ return (int) (q - start);
+}
+
+static int stb_compress_inner(stb_uchar *input, stb_uint length)
+{
+ int literals = 0;
+ stb_uint len,i;
+
+ stb_uchar **chash;
+ chash = (stb_uchar**) malloc(stb__hashsize * sizeof(stb_uchar*));
+ if (chash == NULL) return 0; // failure
+ for (i=0; i < stb__hashsize; ++i)
+ chash[i] = NULL;
+
+ // stream signature
+ stb_out(0x57); stb_out(0xbc);
+ stb_out2(0);
+
+ stb_out4(0); // 64-bit length requires 32-bit leading 0
+ stb_out4(length);
+ stb_out4(stb__window);
+
+ stb__running_adler = 1;
+
+ len = stb_compress_chunk(input, input, input+length, length, &literals, chash, stb__hashsize-1);
+ assert(len == length);
+
+ outliterals(input+length - literals, literals);
+
+ free(chash);
+
+ stb_out2(0x05fa); // end opcode
+
+ stb_out4(stb__running_adler);
+
+ return 1; // success
+}
+
+stb_uint stb_compress(stb_uchar *out, stb_uchar *input, stb_uint length)
+{
+ stb__out = out;
+ stb__outfile = NULL;
+
+ stb_compress_inner(input, length);
+
+ return (stb_uint) (stb__out - out);
+}
+
+int stb_compress_tofile(char *filename, char *input, unsigned int length)
+{
+ //int maxlen = length + 512 + (length >> 2); // total guess
+ //char *buffer = (char *) malloc(maxlen);
+ //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length);
+
+ stb__out = NULL;
+ stb__outfile = stb_p_fopen(filename, "wb");
+ if (!stb__outfile) return 0;
+
+ stb__outbytes = 0;
+
+ if (!stb_compress_inner((stb_uchar*)input, length))
+ return 0;
+
+ fclose(stb__outfile);
+
+ return stb__outbytes;
+}
+
+int stb_compress_intofile(FILE *f, char *input, unsigned int length)
+{
+ //int maxlen = length + 512 + (length >> 2); // total guess
+ //char *buffer = (char*)malloc(maxlen);
+ //int blen = stb_compress((stb_uchar*)buffer, (stb_uchar*)input, length);
+
+ stb__out = NULL;
+ stb__outfile = f;
+ if (!stb__outfile) return 0;
+
+ stb__outbytes = 0;
+
+ if (!stb_compress_inner((stb_uchar*)input, length))
+ return 0;
+
+ return stb__outbytes;
+}
+
+////////////////////// streaming I/O version /////////////////////
+
+
+static size_t stb_out_backpatch_id(void)
+{
+ if (stb__out)
+ return (size_t) stb__out;
+ else
+ return ftell(stb__outfile);
+}
+
+static void stb_out_backpatch(size_t id, stb_uint value)
+{
+ stb_uchar data[4] = { (stb_uchar)(value >> 24), (stb_uchar)(value >> 16), (stb_uchar)(value >> 8), (stb_uchar)(value) };
+ if (stb__out) {
+ memcpy((void *) id, data, 4);
+ } else {
+ stb_uint where = ftell(stb__outfile);
+ fseek(stb__outfile, (long) id, SEEK_SET);
+ fwrite(data, 4, 1, stb__outfile);
+ fseek(stb__outfile, where, SEEK_SET);
+ }
+}
+
+// ok, the wraparound buffer was a total failure. let's instead
+// use a copying-in-place buffer, which lets us share the code.
+// This is way less efficient but it'll do for now.
+
+static struct
+{
+ stb_uchar *buffer;
+ int size; // physical size of buffer in bytes
+
+ int valid; // amount of valid data in bytes
+ int start; // bytes of data already output
+
+ int window;
+ int fsize;
+
+ int pending_literals; // bytes not-quite output but counted in start
+ int length_id;
+
+ stb_uint total_bytes;
+
+ stb_uchar **chash;
+ stb_uint hashmask;
+} xtb;
+
+static int stb_compress_streaming_start(void)
+{
+ stb_uint i;
+ xtb.size = stb__window * 3;
+ xtb.buffer = (stb_uchar*)malloc(xtb.size);
+ if (!xtb.buffer) return 0;
+
+ xtb.chash = (stb_uchar**)malloc(sizeof(*xtb.chash) * stb__hashsize);
+ if (!xtb.chash) {
+ free(xtb.buffer);
+ return 0;
+ }
+
+ for (i=0; i < stb__hashsize; ++i)
+ xtb.chash[i] = NULL;
+
+ xtb.hashmask = stb__hashsize-1;
+
+ xtb.valid = 0;
+ xtb.start = 0;
+ xtb.window = stb__window;
+ xtb.fsize = stb__window;
+ xtb.pending_literals = 0;
+ xtb.total_bytes = 0;
+
+ // stream signature
+ stb_out(0x57); stb_out(0xbc); stb_out2(0);
+
+ stb_out4(0); // 64-bit length requires 32-bit leading 0
+
+ xtb.length_id = (int) stb_out_backpatch_id();
+ stb_out4(0); // we don't know the output length yet
+
+ stb_out4(stb__window);
+
+ stb__running_adler = 1;
+
+ return 1;
+}
+
+static int stb_compress_streaming_end(void)
+{
+ // flush out any remaining data
+ stb_compress_chunk(xtb.buffer, xtb.buffer+xtb.start, xtb.buffer+xtb.valid,
+ xtb.valid-xtb.start, &xtb.pending_literals, xtb.chash, xtb.hashmask);
+
+ // write out pending literals
+ outliterals(xtb.buffer + xtb.valid - xtb.pending_literals, xtb.pending_literals);
+
+ stb_out2(0x05fa); // end opcode
+ stb_out4(stb__running_adler);
+
+ stb_out_backpatch(xtb.length_id, xtb.total_bytes);
+
+ free(xtb.buffer);
+ free(xtb.chash);
+ return 1;
+}
+
+void stb_write(char *data, int data_len)
+{
+ stb_uint i;
+
+ // @TODO: fast path for filling the buffer and doing nothing else
+ // if (xtb.valid + data_len < xtb.size)
+
+ xtb.total_bytes += data_len;
+
+ while (data_len) {
+ // fill buffer
+ if (xtb.valid < xtb.size) {
+ int amt = xtb.size - xtb.valid;
+ if (data_len < amt) amt = data_len;
+ memcpy(xtb.buffer + xtb.valid, data, amt);
+ data_len -= amt;
+ data += amt;
+ xtb.valid += amt;
+ }
+ if (xtb.valid < xtb.size)
+ return;
+
+ // at this point, the buffer is full
+
+ // if we can process some data, go for it; make sure
+ // we leave an 'fsize's worth of data, though
+ if (xtb.start + xtb.fsize < xtb.valid) {
+ int amount = (xtb.valid - xtb.fsize) - xtb.start;
+ int n;
+ assert(amount > 0);
+ n = stb_compress_chunk(xtb.buffer, xtb.buffer + xtb.start, xtb.buffer + xtb.valid,
+ amount, &xtb.pending_literals, xtb.chash, xtb.hashmask);
+ xtb.start += n;
+ }
+
+ assert(xtb.start + xtb.fsize >= xtb.valid);
+ // at this point, our future size is too small, so we
+ // need to flush some history. we, in fact, flush exactly
+ // one window's worth of history
+
+ {
+ int flush = xtb.window;
+ assert(xtb.start >= flush);
+ assert(xtb.valid >= flush);
+
+ // if 'pending literals' extends back into the shift region,
+ // write them out
+ if (xtb.start - xtb.pending_literals < flush) {
+ outliterals(xtb.buffer + xtb.start - xtb.pending_literals, xtb.pending_literals);
+ xtb.pending_literals = 0;
+ }
+
+ // now shift the window
+ memmove(xtb.buffer, xtb.buffer + flush, xtb.valid - flush);
+ xtb.start -= flush;
+ xtb.valid -= flush;
+
+ for (i=0; i <= xtb.hashmask; ++i)
+ if (xtb.chash[i] < xtb.buffer + flush)
+ xtb.chash[i] = NULL;
+ else
+ xtb.chash[i] -= flush;
+ }
+ // and now that we've made room for more data, go back to the top
+ }
+}
+
+int stb_compress_stream_start(FILE *f)
+{
+ stb__out = NULL;
+ stb__outfile = f;
+
+ if (f == NULL)
+ return 0;
+
+ if (!stb_compress_streaming_start())
+ return 0;
+
+ return 1;
+}
+
+void stb_compress_stream_end(int close)
+{
+ stb_compress_streaming_end();
+ if (close && stb__outfile) {
+ fclose(stb__outfile);
+ }
+}
+
+#endif // STB_DEFINE
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// File abstraction... tired of not having this... we can write
+// compressors to be layers over these that auto-close their children.
+
+
+typedef struct stbfile
+{
+ int (*getbyte)(struct stbfile *); // -1 on EOF
+ unsigned int (*getdata)(struct stbfile *, void *block, unsigned int len);
+
+ int (*putbyte)(struct stbfile *, int byte);
+ unsigned int (*putdata)(struct stbfile *, void *block, unsigned int len);
+
+ unsigned int (*size)(struct stbfile *);
+
+ unsigned int (*tell)(struct stbfile *);
+ void (*backpatch)(struct stbfile *, unsigned int tell, void *block, unsigned int len);
+
+ void (*close)(struct stbfile *);
+
+ FILE *f; // file to fread/fwrite
+ unsigned char *buffer; // input/output buffer
+ unsigned char *indata, *inend; // input buffer
+ union {
+ int various;
+ void *ptr;
+ };
+} stbfile;
+
+STB_EXTERN unsigned int stb_getc(stbfile *f); // read
+STB_EXTERN int stb_putc(stbfile *f, int ch); // write
+STB_EXTERN unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len); // read
+STB_EXTERN unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len); // write
+STB_EXTERN unsigned int stb_tell(stbfile *f); // read
+STB_EXTERN unsigned int stb_size(stbfile *f); // read/write
+STB_EXTERN void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len); // write
+
+#ifdef STB_DEFINE
+
+unsigned int stb_getc(stbfile *f) { return f->getbyte(f); }
+int stb_putc(stbfile *f, int ch) { return f->putbyte(f, ch); }
+
+unsigned int stb_getdata(stbfile *f, void *buffer, unsigned int len)
+{
+ return f->getdata(f, buffer, len);
+}
+unsigned int stb_putdata(stbfile *f, void *buffer, unsigned int len)
+{
+ return f->putdata(f, buffer, len);
+}
+void stb_close(stbfile *f)
+{
+ f->close(f);
+ free(f);
+}
+unsigned int stb_tell(stbfile *f) { return f->tell(f); }
+unsigned int stb_size(stbfile *f) { return f->size(f); }
+void stb_backpatch(stbfile *f, unsigned int tell, void *buffer, unsigned int len)
+{
+ f->backpatch(f,tell,buffer,len);
+}
+
+// FILE * implementation
+static int stb__fgetbyte(stbfile *f) { return fgetc(f->f); }
+static int stb__fputbyte(stbfile *f, int ch) { return fputc(ch, f->f)==0; }
+static unsigned int stb__fgetdata(stbfile *f, void *buffer, unsigned int len) { return (unsigned int) fread(buffer,1,len,f->f); }
+static unsigned int stb__fputdata(stbfile *f, void *buffer, unsigned int len) { return (unsigned int) fwrite(buffer,1,len,f->f); }
+static unsigned int stb__fsize(stbfile *f) { return (unsigned int) stb_filelen(f->f); }
+static unsigned int stb__ftell(stbfile *f) { return (unsigned int) ftell(f->f); }
+static void stb__fbackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len)
+{
+ fseek(f->f, where, SEEK_SET);
+ fwrite(buffer, 1, len, f->f);
+ fseek(f->f, 0, SEEK_END);
+}
+static void stb__fclose(stbfile *f) { fclose(f->f); }
+
+stbfile *stb_openf(FILE *f)
+{
+ stbfile m = { stb__fgetbyte, stb__fgetdata,
+ stb__fputbyte, stb__fputdata,
+ stb__fsize, stb__ftell, stb__fbackpatch, stb__fclose,
+ 0,0,0, };
+ stbfile *z = (stbfile *) malloc(sizeof(*z));
+ if (z) {
+ *z = m;
+ z->f = f;
+ }
+ return z;
+}
+
+static int stb__nogetbyte(stbfile *f) { assert(0); return -1; }
+static unsigned int stb__nogetdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; }
+static int stb__noputbyte(stbfile *f, int ch) { assert(0); return 0; }
+static unsigned int stb__noputdata(stbfile *f, void *buffer, unsigned int len) { assert(0); return 0; }
+static void stb__nobackpatch(stbfile *f, unsigned int where, void *buffer, unsigned int len) { assert(0); }
+
+static int stb__bgetbyte(stbfile *s)
+{
+ if (s->indata < s->inend)
+ return *s->indata++;
+ else
+ return -1;
+}
+
+static unsigned int stb__bgetdata(stbfile *s, void *buffer, unsigned int len)
+{
+ if (s->indata + len > s->inend)
+ len = (unsigned int) (s->inend - s->indata);
+ memcpy(buffer, s->indata, len);
+ s->indata += len;
+ return len;
+}
+static unsigned int stb__bsize(stbfile *s) { return (unsigned int) (s->inend - s->buffer); }
+static unsigned int stb__btell(stbfile *s) { return (unsigned int) (s->indata - s->buffer); }
+
+static void stb__bclose(stbfile *s)
+{
+ if (s->various)
+ free(s->buffer);
+}
+
+stbfile *stb_open_inbuffer(void *buffer, unsigned int len)
+{
+ stbfile m = { stb__bgetbyte, stb__bgetdata,
+ stb__noputbyte, stb__noputdata,
+ stb__bsize, stb__btell, stb__nobackpatch, stb__bclose };
+ stbfile *z = (stbfile *) malloc(sizeof(*z));
+ if (z) {
+ *z = m;
+ z->buffer = (unsigned char *) buffer;
+ z->indata = z->buffer;
+ z->inend = z->indata + len;
+ }
+ return z;
+}
+
+stbfile *stb_open_inbuffer_free(void *buffer, unsigned int len)
+{
+ stbfile *z = stb_open_inbuffer(buffer, len);
+ if (z)
+ z->various = 1; // free
+ return z;
+}
+
+#ifndef STB_VERSION
+// if we've been cut-and-pasted elsewhere, you get a limited
+// version of stb_open, without the 'k' flag and utf8 support
+static void stb__fclose2(stbfile *f)
+{
+ fclose(f->f);
+}
+
+stbfile *stb_open(char *filename, char *mode)
+{
+ FILE *f = stb_p_fopen(filename, mode);
+ stbfile *s;
+ if (f == NULL) return NULL;
+ s = stb_openf(f);
+ if (s)
+ s->close = stb__fclose2;
+ return s;
+}
+#else
+// the full version depends on some code in stb.h; this
+// also includes the memory buffer output format implemented with stb_arr
+static void stb__fclose2(stbfile *f)
+{
+ stb_fclose(f->f, f->various);
+}
+
+stbfile *stb_open(char *filename, char *mode)
+{
+ FILE *f = stb_fopen(filename, mode[0] == 'k' ? mode+1 : mode);
+ stbfile *s;
+ if (f == NULL) return NULL;
+ s = stb_openf(f);
+ if (s) {
+ s->close = stb__fclose2;
+ s->various = mode[0] == 'k' ? stb_keep_if_different : stb_keep_yes;
+ }
+ return s;
+}
+
+static int stb__aputbyte(stbfile *f, int ch)
+{
+ stb_arr_push(f->buffer, ch);
+ return 1;
+}
+static unsigned int stb__aputdata(stbfile *f, void *data, unsigned int len)
+{
+ memcpy(stb_arr_addn(f->buffer, (int) len), data, len);
+ return len;
+}
+static unsigned int stb__asize(stbfile *f) { return stb_arr_len(f->buffer); }
+static void stb__abackpatch(stbfile *f, unsigned int where, void *data, unsigned int len)
+{
+ memcpy(f->buffer+where, data, len);
+}
+static void stb__aclose(stbfile *f)
+{
+ *(unsigned char **) f->ptr = f->buffer;
+}
+
+stbfile *stb_open_outbuffer(unsigned char **update_on_close)
+{
+ stbfile m = { stb__nogetbyte, stb__nogetdata,
+ stb__aputbyte, stb__aputdata,
+ stb__asize, stb__asize, stb__abackpatch, stb__aclose };
+ stbfile *z = (stbfile *) malloc(sizeof(*z));
+ if (z) {
+ z->ptr = update_on_close;
+ *z = m;
+ }
+ return z;
+}
+#endif
+#endif
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Arithmetic coder... based on cbloom's notes on the subject, should be
+// less code than a huffman code.
+
+typedef struct
+{
+ unsigned int range_low;
+ unsigned int range_high;
+ unsigned int code, range; // decode
+ int buffered_u8;
+ int pending_ffs;
+ stbfile *output;
+} stb_arith;
+
+STB_EXTERN void stb_arith_init_encode(stb_arith *a, stbfile *out);
+STB_EXTERN void stb_arith_init_decode(stb_arith *a, stbfile *in);
+STB_EXTERN stbfile *stb_arith_encode_close(stb_arith *a);
+STB_EXTERN stbfile *stb_arith_decode_close(stb_arith *a);
+
+STB_EXTERN void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq);
+STB_EXTERN void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq);
+STB_EXTERN unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq);
+STB_EXTERN void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq);
+STB_EXTERN unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2);
+STB_EXTERN void stb_arith_decode_advance_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq);
+
+STB_EXTERN void stb_arith_encode_byte(stb_arith *a, int byte);
+STB_EXTERN int stb_arith_decode_byte(stb_arith *a);
+
+// this is a memory-inefficient way of doing things, but it's
+// fast(?) and simple
+typedef struct
+{
+ unsigned short cumfreq;
+ unsigned short samples;
+} stb_arith_symstate_item;
+
+typedef struct
+{
+ int num_sym;
+ unsigned int pow2;
+ int countdown;
+ stb_arith_symstate_item data[1];
+} stb_arith_symstate;
+
+#ifdef STB_DEFINE
+void stb_arith_init_encode(stb_arith *a, stbfile *out)
+{
+ a->range_low = 0;
+ a->range_high = 0xffffffff;
+ a->pending_ffs = -1; // means no buffered character currently, to speed up normal case
+ a->output = out;
+}
+
+static void stb__arith_carry(stb_arith *a)
+{
+ int i;
+ assert(a->pending_ffs != -1); // can't carry with no data
+ stb_putc(a->output, a->buffered_u8);
+ for (i=0; i < a->pending_ffs; ++i)
+ stb_putc(a->output, 0);
+}
+
+static void stb__arith_putbyte(stb_arith *a, int byte)
+{
+ if (a->pending_ffs) {
+ if (a->pending_ffs == -1) { // means no buffered data; encoded for fast path efficiency
+ if (byte == 0xff)
+ stb_putc(a->output, byte); // just write it immediately
+ else {
+ a->buffered_u8 = byte;
+ a->pending_ffs = 0;
+ }
+ } else if (byte == 0xff) {
+ ++a->pending_ffs;
+ } else {
+ int i;
+ stb_putc(a->output, a->buffered_u8);
+ for (i=0; i < a->pending_ffs; ++i)
+ stb_putc(a->output, 0xff);
+ }
+ } else if (byte == 0xff) {
+ ++a->pending_ffs;
+ } else {
+ // fast path
+ stb_putc(a->output, a->buffered_u8);
+ a->buffered_u8 = byte;
+ }
+}
+
+static void stb__arith_flush(stb_arith *a)
+{
+ if (a->pending_ffs >= 0) {
+ int i;
+ stb_putc(a->output, a->buffered_u8);
+ for (i=0; i < a->pending_ffs; ++i)
+ stb_putc(a->output, 0xff);
+ }
+}
+
+static void stb__renorm_encoder(stb_arith *a)
+{
+ stb__arith_putbyte(a, a->range_low >> 24);
+ a->range_low <<= 8;
+ a->range_high = (a->range_high << 8) | 0xff;
+}
+
+static void stb__renorm_decoder(stb_arith *a)
+{
+ int c = stb_getc(a->output);
+ a->code = (a->code << 8) + (c >= 0 ? c : 0); // if EOF, insert 0
+}
+
+void stb_arith_encode(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq)
+{
+ unsigned int range = a->range_high - a->range_low;
+ unsigned int old = a->range_low;
+ range /= totalfreq;
+ a->range_low += range * cumfreq;
+ a->range_high = a->range_low + range*freq;
+ if (a->range_low < old)
+ stb__arith_carry(a);
+ while (a->range_high - a->range_low < 0x1000000)
+ stb__renorm_encoder(a);
+}
+
+void stb_arith_encode_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq)
+{
+ unsigned int range = a->range_high - a->range_low;
+ unsigned int old = a->range_low;
+ range >>= totalfreq2;
+ a->range_low += range * cumfreq;
+ a->range_high = a->range_low + range*freq;
+ if (a->range_low < old)
+ stb__arith_carry(a);
+ while (a->range_high - a->range_low < 0x1000000)
+ stb__renorm_encoder(a);
+}
+
+unsigned int stb_arith_decode_value(stb_arith *a, unsigned int totalfreq)
+{
+ unsigned int freqsize = a->range / totalfreq;
+ unsigned int z = a->code / freqsize;
+ return z >= totalfreq ? totalfreq-1 : z;
+}
+
+void stb_arith_decode_advance(stb_arith *a, unsigned int totalfreq, unsigned int freq, unsigned int cumfreq)
+{
+ unsigned int freqsize = a->range / totalfreq; // @OPTIMIZE, share with above divide somehow?
+ a->code -= freqsize * cumfreq;
+ a->range = freqsize * freq;
+ while (a->range < 0x1000000)
+ stb__renorm_decoder(a);
+}
+
+unsigned int stb_arith_decode_value_log2(stb_arith *a, unsigned int totalfreq2)
+{
+ unsigned int freqsize = a->range >> totalfreq2;
+ unsigned int z = a->code / freqsize;
+ return z >= (1U<<totalfreq2) ? (1U<<totalfreq2)-1 : z;
+}
+
+void stb_arith_decode_advance_log2(stb_arith *a, unsigned int totalfreq2, unsigned int freq, unsigned int cumfreq)
+{
+ unsigned int freqsize = a->range >> totalfreq2;
+ a->code -= freqsize * cumfreq;
+ a->range = freqsize * freq;
+ while (a->range < 0x1000000)
+ stb__renorm_decoder(a);
+}
+
+stbfile *stb_arith_encode_close(stb_arith *a)
+{
+ // put exactly as many bytes as we'll read, so we can turn on/off arithmetic coding in a stream
+ stb__arith_putbyte(a, a->range_low >> 24);
+ stb__arith_putbyte(a, a->range_low >> 16);
+ stb__arith_putbyte(a, a->range_low >> 8);
+ stb__arith_putbyte(a, a->range_low >> 0);
+ stb__arith_flush(a);
+ return a->output;
+}
+
+stbfile *stb_arith_decode_close(stb_arith *a)
+{
+ return a->output;
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Threads
+//
+
+#ifndef _WIN32
+#ifdef STB_THREADS
+#error "threads not implemented except for Windows"
+#endif
+#endif
+
+// call this function to free any global variables for memory testing
+STB_EXTERN void stb_thread_cleanup(void);
+
+typedef void * (*stb_thread_func)(void *);
+
+// do not rely on these types, this is an implementation detail.
+// compare against STB_THREAD_NULL and ST_SEMAPHORE_NULL
+typedef void *stb_thread;
+typedef void *stb_semaphore;
+typedef void *stb_mutex;
+typedef struct stb__sync *stb_sync;
+
+#define STB_SEMAPHORE_NULL NULL
+#define STB_THREAD_NULL NULL
+#define STB_MUTEX_NULL NULL
+#define STB_SYNC_NULL NULL
+
+// get the number of processors (limited to those in the affinity mask for this process).
+STB_EXTERN int stb_processor_count(void);
+// force to run on a single core -- needed for RDTSC to work, e.g. for iprof
+STB_EXTERN void stb_force_uniprocessor(void);
+
+// stb_work functions: queue up work to be done by some worker threads
+
+// set number of threads to serve the queue; you can change this on the fly,
+// but if you decrease it, it won't decrease until things currently on the
+// queue are finished
+STB_EXTERN void stb_work_numthreads(int n);
+// set maximum number of units in the queue; you can only set this BEFORE running any work functions
+STB_EXTERN int stb_work_maxunits(int n);
+// enqueue some work to be done (can do this from any thread, or even from a piece of work);
+// return value of f is stored in *return_code if non-NULL
+STB_EXTERN int stb_work(stb_thread_func f, void *d, volatile void **return_code);
+// as above, but stb_sync_reach is called on 'rel' after work is complete
+STB_EXTERN int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel);
+
+
+// necessary to call this when using volatile to order writes/reads
+STB_EXTERN void stb_barrier(void);
+
+// support for independent queues with their own threads
+
+typedef struct stb__workqueue stb_workqueue;
+
+STB_EXTERN stb_workqueue*stb_workq_new(int numthreads, int max_units);
+STB_EXTERN stb_workqueue*stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex);
+STB_EXTERN void stb_workq_delete(stb_workqueue *q);
+STB_EXTERN void stb_workq_numthreads(stb_workqueue *q, int n);
+STB_EXTERN int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code);
+STB_EXTERN int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel);
+STB_EXTERN int stb_workq_length(stb_workqueue *q);
+
+STB_EXTERN stb_thread stb_create_thread (stb_thread_func f, void *d);
+STB_EXTERN stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel);
+STB_EXTERN void stb_destroy_thread(stb_thread t);
+
+STB_EXTERN stb_semaphore stb_sem_new(int max_val);
+STB_EXTERN stb_semaphore stb_sem_new_extra(int max_val, int start_val);
+STB_EXTERN void stb_sem_delete (stb_semaphore s);
+STB_EXTERN void stb_sem_waitfor(stb_semaphore s);
+STB_EXTERN void stb_sem_release(stb_semaphore s);
+
+STB_EXTERN stb_mutex stb_mutex_new(void);
+STB_EXTERN void stb_mutex_delete(stb_mutex m);
+STB_EXTERN void stb_mutex_begin(stb_mutex m);
+STB_EXTERN void stb_mutex_end(stb_mutex m);
+
+STB_EXTERN stb_sync stb_sync_new(void);
+STB_EXTERN void stb_sync_delete(stb_sync s);
+STB_EXTERN int stb_sync_set_target(stb_sync s, int count);
+STB_EXTERN void stb_sync_reach_and_wait(stb_sync s); // wait for 'target' reachers
+STB_EXTERN int stb_sync_reach(stb_sync s);
+
+typedef struct stb__threadqueue stb_threadqueue;
+#define STB_THREADQ_DYNAMIC 0
+STB_EXTERN stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove);
+STB_EXTERN void stb_threadq_delete(stb_threadqueue *tq);
+STB_EXTERN int stb_threadq_get(stb_threadqueue *tq, void *output);
+STB_EXTERN void stb_threadq_get_block(stb_threadqueue *tq, void *output);
+STB_EXTERN int stb_threadq_add(stb_threadqueue *tq, void *input);
+// can return FALSE if STB_THREADQ_DYNAMIC and attempt to grow fails
+STB_EXTERN int stb_threadq_add_block(stb_threadqueue *tq, void *input);
+
+#ifdef STB_THREADS
+#ifdef STB_DEFINE
+
+typedef struct
+{
+ stb_thread_func f;
+ void *d;
+ volatile void **return_val;
+ stb_semaphore sem;
+} stb__thread;
+
+// this is initialized along all possible paths to create threads, therefore
+// it's always initialized before any other threads are create, therefore
+// it's free of races AS LONG AS you only create threads through stb_*
+static stb_mutex stb__threadmutex, stb__workmutex;
+
+static void stb__threadmutex_init(void)
+{
+ if (stb__threadmutex == STB_SEMAPHORE_NULL) {
+ stb__threadmutex = stb_mutex_new();
+ stb__workmutex = stb_mutex_new();
+ }
+}
+
+#ifdef STB_THREAD_TEST
+volatile float stb__t1=1, stb__t2;
+
+static void stb__wait(int n)
+{
+ float z = 0;
+ int i;
+ for (i=0; i < n; ++i)
+ z += 1 / (stb__t1+i);
+ stb__t2 = z;
+}
+#else
+#define stb__wait(x)
+#endif
+
+#ifdef _WIN32
+
+// avoid including windows.h -- note that our definitions aren't
+// exactly the same (we don't define the security descriptor struct)
+// so if you want to include windows.h, make sure you do it first.
+#include <process.h>
+
+#ifndef _WINDOWS_ // check windows.h guard
+#define STB__IMPORT STB_EXTERN __declspec(dllimport)
+#define STB__DW unsigned long
+
+STB__IMPORT int __stdcall TerminateThread(void *, STB__DW);
+STB__IMPORT void * __stdcall CreateSemaphoreA(void *sec, long,long,char*);
+STB__IMPORT int __stdcall CloseHandle(void *);
+STB__IMPORT STB__DW __stdcall WaitForSingleObject(void *, STB__DW);
+STB__IMPORT int __stdcall ReleaseSemaphore(void *, long, long *);
+STB__IMPORT void __stdcall Sleep(STB__DW);
+#endif
+
+// necessary to call this when using volatile to order writes/reads
+void stb_barrier(void)
+{
+ #ifdef MemoryBarrier
+ MemoryBarrier();
+ #else
+ long temp;
+ __asm xchg temp,eax;
+ #endif
+}
+
+static void stb__thread_run(void *t)
+{
+ void *res;
+ stb__thread info = * (stb__thread *) t;
+ free(t);
+ res = info.f(info.d);
+ if (info.return_val)
+ *info.return_val = res;
+ if (info.sem != STB_SEMAPHORE_NULL)
+ stb_sem_release(info.sem);
+}
+
+static stb_thread stb_create_thread_raw(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel)
+{
+#ifdef _MT
+#if defined(STB_FASTMALLOC) && !defined(STB_FASTMALLOC_ITS_OKAY_I_ONLY_MALLOC_IN_ONE_THREAD)
+ stb_fatal("Error! Cannot use STB_FASTMALLOC with threads.\n");
+ return STB_THREAD_NULL;
+#else
+ unsigned long id;
+ stb__thread *data = (stb__thread *) malloc(sizeof(*data));
+ if (!data) return NULL;
+ stb__threadmutex_init();
+ data->f = f;
+ data->d = d;
+ data->return_val = return_code;
+ data->sem = rel;
+ id = _beginthread(stb__thread_run, 0, data);
+ if (id == -1) return NULL;
+ return (void *) id;
+#endif
+#else
+#ifdef STB_NO_STB_STRINGS
+ stb_fatal("Invalid compilation");
+#else
+ stb_fatal("Must compile mult-threaded to use stb_thread/stb_work.");
+#endif
+ return NULL;
+#endif
+}
+
+// trivial win32 wrappers
+void stb_destroy_thread(stb_thread t) { TerminateThread(t,0); }
+stb_semaphore stb_sem_new(int maxv) {return CreateSemaphoreA(NULL,0,maxv,NULL); }
+stb_semaphore stb_sem_new_extra(int maxv,int start){return CreateSemaphoreA(NULL,start,maxv,NULL); }
+void stb_sem_delete(stb_semaphore s) { if (s != NULL) CloseHandle(s); }
+void stb_sem_waitfor(stb_semaphore s) { WaitForSingleObject(s, 0xffffffff); } // INFINITE
+void stb_sem_release(stb_semaphore s) { ReleaseSemaphore(s,1,NULL); }
+static void stb__thread_sleep(int ms) { Sleep(ms); }
+
+#ifndef _WINDOWS_
+STB__IMPORT int __stdcall GetProcessAffinityMask(void *, STB__DW *, STB__DW *);
+STB__IMPORT void * __stdcall GetCurrentProcess(void);
+STB__IMPORT int __stdcall SetProcessAffinityMask(void *, STB__DW);
+#endif
+
+int stb_processor_count(void)
+{
+ unsigned long proc,sys;
+ GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys);
+ return stb_bitcount(proc);
+}
+
+void stb_force_uniprocessor(void)
+{
+ unsigned long proc,sys;
+ GetProcessAffinityMask(GetCurrentProcess(), &proc, &sys);
+ if (stb_bitcount(proc) > 1) {
+ int z;
+ for (z=0; z < 32; ++z)
+ if (proc & (1 << z))
+ break;
+ if (z < 32) {
+ proc = 1 << z;
+ SetProcessAffinityMask(GetCurrentProcess(), proc);
+ }
+ }
+}
+
+#ifdef _WINDOWS_
+#define STB_MUTEX_NATIVE
+void *stb_mutex_new(void)
+{
+ CRITICAL_SECTION *p = (CRITICAL_SECTION *) malloc(sizeof(*p));
+ if (p)
+#if _WIN32_WINNT >= 0x0500
+ InitializeCriticalSectionAndSpinCount(p, 500);
+#else
+ InitializeCriticalSection(p);
+#endif
+ return p;
+}
+
+void stb_mutex_delete(void *p)
+{
+ if (p) {
+ DeleteCriticalSection((CRITICAL_SECTION *) p);
+ free(p);
+ }
+}
+
+void stb_mutex_begin(void *p)
+{
+ stb__wait(500);
+ if (p)
+ EnterCriticalSection((CRITICAL_SECTION *) p);
+}
+
+void stb_mutex_end(void *p)
+{
+ if (p)
+ LeaveCriticalSection((CRITICAL_SECTION *) p);
+ stb__wait(500);
+}
+#endif // _WINDOWS_
+
+#if 0
+// for future reference,
+// InterlockedCompareExchange for x86:
+ int cas64_mp(void * dest, void * xcmp, void * xxchg) {
+ __asm
+ {
+ mov esi, [xxchg] ; exchange
+ mov ebx, [esi + 0]
+ mov ecx, [esi + 4]
+
+ mov esi, [xcmp] ; comparand
+ mov eax, [esi + 0]
+ mov edx, [esi + 4]
+
+ mov edi, [dest] ; destination
+ lock cmpxchg8b [edi]
+ jz yyyy;
+
+ mov [esi + 0], eax;
+ mov [esi + 4], edx;
+
+yyyy:
+ xor eax, eax;
+ setz al;
+ };
+
+inline unsigned __int64 _InterlockedCompareExchange64(volatile unsigned __int64 *dest
+ ,unsigned __int64 exchange
+ ,unsigned __int64 comperand)
+{
+ //value returned in eax::edx
+ __asm {
+ lea esi,comperand;
+ lea edi,exchange;
+
+ mov eax,[esi];
+ mov edx,4[esi];
+ mov ebx,[edi];
+ mov ecx,4[edi];
+ mov esi,dest;
+ lock CMPXCHG8B [esi];
+ }
+#endif // #if 0
+
+#endif // _WIN32
+
+stb_thread stb_create_thread2(stb_thread_func f, void *d, volatile void **return_code, stb_semaphore rel)
+{
+ return stb_create_thread_raw(f,d,return_code,rel);
+}
+
+stb_thread stb_create_thread(stb_thread_func f, void *d)
+{
+ return stb_create_thread2(f,d,NULL,STB_SEMAPHORE_NULL);
+}
+
+// mutex implemented by wrapping semaphore
+#ifndef STB_MUTEX_NATIVE
+stb_mutex stb_mutex_new(void) { return stb_sem_new_extra(1,1); }
+void stb_mutex_delete(stb_mutex m) { stb_sem_delete (m); }
+void stb_mutex_begin(stb_mutex m) { stb__wait(500); if (m) stb_sem_waitfor(m); }
+void stb_mutex_end(stb_mutex m) { if (m) stb_sem_release(m); stb__wait(500); }
+#endif
+
+// thread merge operation
+struct stb__sync
+{
+ int target; // target number of threads to hit it
+ int sofar; // total threads that hit it
+ int waiting; // total threads waiting
+
+ stb_mutex start; // mutex to prevent starting again before finishing previous
+ stb_mutex mutex; // mutex while tweaking state
+ stb_semaphore release; // semaphore wake up waiting threads
+ // we have to wake them up one at a time, rather than using a single release
+ // call, because win32 semaphores don't let you dynamically change the max count!
+};
+
+stb_sync stb_sync_new(void)
+{
+ stb_sync s = (stb_sync) malloc(sizeof(*s));
+ if (!s) return s;
+
+ s->target = s->sofar = s->waiting = 0;
+ s->mutex = stb_mutex_new();
+ s->start = stb_mutex_new();
+ s->release = stb_sem_new(1);
+ if (s->mutex == STB_MUTEX_NULL || s->release == STB_SEMAPHORE_NULL || s->start == STB_MUTEX_NULL) {
+ stb_mutex_delete(s->mutex);
+ stb_mutex_delete(s->mutex);
+ stb_sem_delete(s->release);
+ free(s);
+ return NULL;
+ }
+ return s;
+}
+
+void stb_sync_delete(stb_sync s)
+{
+ if (s->waiting) {
+ // it's bad to delete while there are threads waiting!
+ // shall we wait for them to reach, or just bail? just bail
+ assert(0);
+ }
+ stb_mutex_delete(s->mutex);
+ stb_mutex_delete(s->release);
+ free(s);
+}
+
+int stb_sync_set_target(stb_sync s, int count)
+{
+ // don't allow setting a target until the last one is fully released;
+ // note that this can lead to inefficient pipelining, and maybe we'd
+ // be better off ping-ponging between two internal syncs?
+ // I tried seeing how often this happened using TryEnterCriticalSection
+ // and could _never_ get it to happen in imv(stb), even with more threads
+ // than processors. So who knows!
+ stb_mutex_begin(s->start);
+
+ // this mutex is pointless, since it's not valid for threads
+ // to call reach() before anyone calls set_target() anyway
+ stb_mutex_begin(s->mutex);
+
+ assert(s->target == 0); // enforced by start mutex
+ s->target = count;
+ s->sofar = 0;
+ s->waiting = 0;
+ stb_mutex_end(s->mutex);
+ return STB_TRUE;
+}
+
+void stb__sync_release(stb_sync s)
+{
+ if (s->waiting)
+ stb_sem_release(s->release);
+ else {
+ s->target = 0;
+ stb_mutex_end(s->start);
+ }
+}
+
+int stb_sync_reach(stb_sync s)
+{
+ int n;
+ stb_mutex_begin(s->mutex);
+ assert(s->sofar < s->target);
+ n = ++s->sofar; // record this value to avoid possible race if we did 'return s->sofar';
+ if (s->sofar == s->target)
+ stb__sync_release(s);
+ stb_mutex_end(s->mutex);
+ return n;
+}
+
+void stb_sync_reach_and_wait(stb_sync s)
+{
+ stb_mutex_begin(s->mutex);
+ assert(s->sofar < s->target);
+ ++s->sofar;
+ if (s->sofar == s->target) {
+ stb__sync_release(s);
+ stb_mutex_end(s->mutex);
+ } else {
+ ++s->waiting; // we're waiting, so one more waiter
+ stb_mutex_end(s->mutex); // release the mutex to other threads
+
+ stb_sem_waitfor(s->release); // wait for merge completion
+
+ stb_mutex_begin(s->mutex); // on merge completion, grab the mutex
+ --s->waiting; // we're done waiting
+ stb__sync_release(s); // restart the next waiter
+ stb_mutex_end(s->mutex); // and now we're done
+ // this ends the same as the first case, but it's a lot
+ // clearer to understand without sharing the code
+ }
+}
+
+struct stb__threadqueue
+{
+ stb_mutex add, remove;
+ stb_semaphore nonempty, nonfull;
+ int head_blockers; // number of threads blocking--used to know whether to release(avail)
+ int tail_blockers;
+ int head, tail, array_size, growable;
+ int item_size;
+ char *data;
+};
+
+static int stb__tq_wrap(volatile stb_threadqueue *z, int p)
+{
+ if (p == z->array_size)
+ return p - z->array_size;
+ else
+ return p;
+}
+
+int stb__threadq_get_raw(stb_threadqueue *tq2, void *output, int block)
+{
+ volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2;
+ if (tq->head == tq->tail && !block) return 0;
+
+ stb_mutex_begin(tq->remove);
+
+ while (tq->head == tq->tail) {
+ if (!block) {
+ stb_mutex_end(tq->remove);
+ return 0;
+ }
+ ++tq->head_blockers;
+ stb_mutex_end(tq->remove);
+
+ stb_sem_waitfor(tq->nonempty);
+
+ stb_mutex_begin(tq->remove);
+ --tq->head_blockers;
+ }
+
+ memcpy(output, tq->data + tq->head*tq->item_size, tq->item_size);
+ stb_barrier();
+ tq->head = stb__tq_wrap(tq, tq->head+1);
+
+ stb_sem_release(tq->nonfull);
+ if (tq->head_blockers) // can't check if actually non-empty due to race?
+ stb_sem_release(tq->nonempty); // if there are other blockers, wake one
+
+ stb_mutex_end(tq->remove);
+ return STB_TRUE;
+}
+
+int stb__threadq_grow(volatile stb_threadqueue *tq)
+{
+ int n;
+ char *p;
+ assert(tq->remove != STB_MUTEX_NULL); // must have this to allow growth!
+ stb_mutex_begin(tq->remove);
+
+ n = tq->array_size * 2;
+ p = (char *) realloc(tq->data, n * tq->item_size);
+ if (p == NULL) {
+ stb_mutex_end(tq->remove);
+ stb_mutex_end(tq->add);
+ return STB_FALSE;
+ }
+ if (tq->tail < tq->head) {
+ memcpy(p + tq->array_size * tq->item_size, p, tq->tail * tq->item_size);
+ tq->tail += tq->array_size;
+ }
+ tq->data = p;
+ tq->array_size = n;
+
+ stb_mutex_end(tq->remove);
+ return STB_TRUE;
+}
+
+int stb__threadq_add_raw(stb_threadqueue *tq2, void *input, int block)
+{
+ int tail,pos;
+ volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2;
+ stb_mutex_begin(tq->add);
+ for(;;) {
+ pos = tq->tail;
+ tail = stb__tq_wrap(tq, pos+1);
+ if (tail != tq->head) break;
+
+ // full
+ if (tq->growable) {
+ if (!stb__threadq_grow(tq)) {
+ stb_mutex_end(tq->add);
+ return STB_FALSE; // out of memory
+ }
+ } else if (!block) {
+ stb_mutex_end(tq->add);
+ return STB_FALSE;
+ } else {
+ ++tq->tail_blockers;
+ stb_mutex_end(tq->add);
+
+ stb_sem_waitfor(tq->nonfull);
+
+ stb_mutex_begin(tq->add);
+ --tq->tail_blockers;
+ }
+ }
+ memcpy(tq->data + tq->item_size * pos, input, tq->item_size);
+ stb_barrier();
+ tq->tail = tail;
+ stb_sem_release(tq->nonempty);
+ if (tq->tail_blockers) // can't check if actually non-full due to race?
+ stb_sem_release(tq->nonfull);
+ stb_mutex_end(tq->add);
+ return STB_TRUE;
+}
+
+int stb_threadq_length(stb_threadqueue *tq2)
+{
+ int a,b,n;
+ volatile stb_threadqueue *tq = (volatile stb_threadqueue *) tq2;
+ stb_mutex_begin(tq->add);
+ a = tq->head;
+ b = tq->tail;
+ n = tq->array_size;
+ stb_mutex_end(tq->add);
+ if (a > b) b += n;
+ return b-a;
+}
+
+int stb_threadq_get(stb_threadqueue *tq, void *output)
+{
+ return stb__threadq_get_raw(tq, output, STB_FALSE);
+}
+
+void stb_threadq_get_block(stb_threadqueue *tq, void *output)
+{
+ stb__threadq_get_raw(tq, output, STB_TRUE);
+}
+
+int stb_threadq_add(stb_threadqueue *tq, void *input)
+{
+ return stb__threadq_add_raw(tq, input, STB_FALSE);
+}
+
+int stb_threadq_add_block(stb_threadqueue *tq, void *input)
+{
+ return stb__threadq_add_raw(tq, input, STB_TRUE);
+}
+
+void stb_threadq_delete(stb_threadqueue *tq)
+{
+ if (tq) {
+ free(tq->data);
+ stb_mutex_delete(tq->add);
+ stb_mutex_delete(tq->remove);
+ stb_sem_delete(tq->nonempty);
+ stb_sem_delete(tq->nonfull);
+ free(tq);
+ }
+}
+
+#define STB_THREADQUEUE_DYNAMIC 0
+stb_threadqueue *stb_threadq_new(int item_size, int num_items, int many_add, int many_remove)
+{
+ int error=0;
+ stb_threadqueue *tq = (stb_threadqueue *) malloc(sizeof(*tq));
+ if (tq == NULL) return NULL;
+
+ if (num_items == STB_THREADQUEUE_DYNAMIC) {
+ tq->growable = STB_TRUE;
+ num_items = 32;
+ } else
+ tq->growable = STB_FALSE;
+
+ tq->item_size = item_size;
+ tq->array_size = num_items+1;
+
+ tq->add = tq->remove = STB_MUTEX_NULL;
+ tq->nonempty = tq->nonfull = STB_SEMAPHORE_NULL;
+ tq->data = NULL;
+ if (many_add)
+ { tq->add = stb_mutex_new(); if (tq->add == STB_MUTEX_NULL) goto error; }
+ if (many_remove || tq->growable)
+ { tq->remove = stb_mutex_new(); if (tq->remove == STB_MUTEX_NULL) goto error; }
+ tq->nonempty = stb_sem_new(1); if (tq->nonempty == STB_SEMAPHORE_NULL) goto error;
+ tq->nonfull = stb_sem_new(1); if (tq->nonfull == STB_SEMAPHORE_NULL) goto error;
+ tq->data = (char *) malloc(tq->item_size * tq->array_size);
+ if (tq->data == NULL) goto error;
+
+ tq->head = tq->tail = 0;
+ tq->head_blockers = tq->tail_blockers = 0;
+
+ return tq;
+
+error:
+ stb_threadq_delete(tq);
+ return NULL;
+}
+
+typedef struct
+{
+ stb_thread_func f;
+ void *d;
+ volatile void **retval;
+ stb_sync sync;
+} stb__workinfo;
+
+//static volatile stb__workinfo *stb__work;
+
+struct stb__workqueue
+{
+ int numthreads;
+ stb_threadqueue *tq;
+};
+
+static stb_workqueue *stb__work_global;
+
+static void *stb__thread_workloop(void *p)
+{
+ volatile stb_workqueue *q = (volatile stb_workqueue *) p;
+ for(;;) {
+ void *z;
+ stb__workinfo w;
+ stb_threadq_get_block(q->tq, &w);
+ if (w.f == NULL) // null work is a signal to end the thread
+ return NULL;
+ z = w.f(w.d);
+ if (w.retval) { stb_barrier(); *w.retval = z; }
+ if (w.sync != STB_SYNC_NULL) stb_sync_reach(w.sync);
+ }
+}
+
+stb_workqueue *stb_workq_new(int num_threads, int max_units)
+{
+ return stb_workq_new_flags(num_threads, max_units, 0,0);
+}
+
+stb_workqueue *stb_workq_new_flags(int numthreads, int max_units, int no_add_mutex, int no_remove_mutex)
+{
+ stb_workqueue *q = (stb_workqueue *) malloc(sizeof(*q));
+ if (q == NULL) return NULL;
+ q->tq = stb_threadq_new(sizeof(stb__workinfo), max_units, !no_add_mutex, !no_remove_mutex);
+ if (q->tq == NULL) { free(q); return NULL; }
+ q->numthreads = 0;
+ stb_workq_numthreads(q, numthreads);
+ return q;
+}
+
+void stb_workq_delete(stb_workqueue *q)
+{
+ while (stb_workq_length(q) != 0)
+ stb__thread_sleep(1);
+ stb_threadq_delete(q->tq);
+ free(q);
+}
+
+static int stb__work_maxitems = STB_THREADQUEUE_DYNAMIC;
+
+static void stb_work_init(int num_threads)
+{
+ if (stb__work_global == NULL) {
+ stb__threadmutex_init();
+ stb_mutex_begin(stb__workmutex);
+ stb_barrier();
+ if (*(stb_workqueue * volatile *) &stb__work_global == NULL)
+ stb__work_global = stb_workq_new(num_threads, stb__work_maxitems);
+ stb_mutex_end(stb__workmutex);
+ }
+}
+
+static int stb__work_raw(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel)
+{
+ stb__workinfo w;
+ if (q == NULL) {
+ stb_work_init(1);
+ q = stb__work_global;
+ }
+ w.f = f;
+ w.d = d;
+ w.retval = return_code;
+ w.sync = rel;
+ return stb_threadq_add(q->tq, &w);
+}
+
+int stb_workq_length(stb_workqueue *q)
+{
+ return stb_threadq_length(q->tq);
+}
+
+int stb_workq(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code)
+{
+ if (f == NULL) return 0;
+ return stb_workq_reach(q, f, d, return_code, NULL);
+}
+
+int stb_workq_reach(stb_workqueue *q, stb_thread_func f, void *d, volatile void **return_code, stb_sync rel)
+{
+ if (f == NULL) return 0;
+ return stb__work_raw(q, f, d, return_code, rel);
+}
+
+static void stb__workq_numthreads(stb_workqueue *q, int n)
+{
+ while (q->numthreads < n) {
+ stb_create_thread(stb__thread_workloop, q);
+ ++q->numthreads;
+ }
+ while (q->numthreads > n) {
+ stb__work_raw(q, NULL, NULL, NULL, NULL);
+ --q->numthreads;
+ }
+}
+
+void stb_workq_numthreads(stb_workqueue *q, int n)
+{
+ stb_mutex_begin(stb__threadmutex);
+ stb__workq_numthreads(q,n);
+ stb_mutex_end(stb__threadmutex);
+}
+
+int stb_work_maxunits(int n)
+{
+ if (stb__work_global == NULL) {
+ stb__work_maxitems = n;
+ stb_work_init(1);
+ }
+ return stb__work_maxitems;
+}
+
+int stb_work(stb_thread_func f, void *d, volatile void **return_code)
+{
+ return stb_workq(stb__work_global, f,d,return_code);
+}
+
+int stb_work_reach(stb_thread_func f, void *d, volatile void **return_code, stb_sync rel)
+{
+ return stb_workq_reach(stb__work_global, f,d,return_code,rel);
+}
+
+void stb_work_numthreads(int n)
+{
+ if (stb__work_global == NULL)
+ stb_work_init(n);
+ else
+ stb_workq_numthreads(stb__work_global, n);
+}
+#endif // STB_DEFINE
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Background disk I/O
+//
+//
+
+#define STB_BGIO_READ_ALL (-1)
+STB_EXTERN int stb_bgio_read (char *filename, int offset, int len, stb_uchar **result, int *olen);
+STB_EXTERN int stb_bgio_readf (FILE *f , int offset, int len, stb_uchar **result, int *olen);
+STB_EXTERN int stb_bgio_read_to (char *filename, int offset, int len, stb_uchar *buffer, int *olen);
+STB_EXTERN int stb_bgio_readf_to(FILE *f , int offset, int len, stb_uchar *buffer, int *olen);
+
+typedef struct
+{
+ int have_data;
+ int is_valid;
+ int is_dir;
+ time_t filetime;
+ stb_int64 filesize;
+} stb_bgstat;
+
+STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result);
+
+#ifdef STB_DEFINE
+
+static stb_workqueue *stb__diskio;
+static stb_mutex stb__diskio_mutex;
+
+void stb_thread_cleanup(void)
+{
+ if (stb__work_global) stb_workq_delete(stb__work_global); stb__work_global = NULL;
+ if (stb__threadmutex) stb_mutex_delete(stb__threadmutex); stb__threadmutex = NULL;
+ if (stb__workmutex) stb_mutex_delete(stb__workmutex); stb__workmutex = NULL;
+ if (stb__diskio) stb_workq_delete(stb__diskio); stb__diskio = NULL;
+ if (stb__diskio_mutex)stb_mutex_delete(stb__diskio_mutex);stb__diskio_mutex= NULL;
+}
+
+
+typedef struct
+{
+ char *filename;
+ FILE *f;
+ int offset;
+ int len;
+
+ stb_bgstat *stat_out;
+ stb_uchar *output;
+ stb_uchar **result;
+ int *len_output;
+ int *flag;
+} stb__disk_command;
+
+#define STB__MAX_DISK_COMMAND 100
+static stb__disk_command stb__dc_queue[STB__MAX_DISK_COMMAND];
+static int stb__dc_offset;
+
+void stb__io_init(void)
+{
+ if (!stb__diskio) {
+ stb__threadmutex_init();
+ stb_mutex_begin(stb__threadmutex);
+ stb_barrier();
+ if (*(stb_thread * volatile *) &stb__diskio == NULL) {
+ stb__diskio_mutex = stb_mutex_new();
+ // use many threads so OS can try to schedule seeks
+ stb__diskio = stb_workq_new_flags(16,STB__MAX_DISK_COMMAND,STB_FALSE,STB_FALSE);
+ }
+ stb_mutex_end(stb__threadmutex);
+ }
+}
+
+static void * stb__io_error(stb__disk_command *dc)
+{
+ if (dc->len_output) *dc->len_output = 0;
+ if (dc->result) *dc->result = NULL;
+ if (dc->flag) *dc->flag = -1;
+ return NULL;
+}
+
+static void * stb__io_task(void *p)
+{
+ stb__disk_command *dc = (stb__disk_command *) p;
+ int len;
+ FILE *f;
+ stb_uchar *buf;
+
+ if (dc->stat_out) {
+ struct _stati64 s;
+ if (!_stati64(dc->filename, &s)) {
+ dc->stat_out->filesize = s.st_size;
+ dc->stat_out->filetime = s.st_mtime;
+ dc->stat_out->is_dir = s.st_mode & _S_IFDIR;
+ dc->stat_out->is_valid = (s.st_mode & _S_IFREG) || dc->stat_out->is_dir;
+ } else
+ dc->stat_out->is_valid = 0;
+ stb_barrier();
+ dc->stat_out->have_data = 1;
+ free(dc->filename);
+ return 0;
+ }
+ if (dc->f) {
+ #ifdef WIN32
+ f = _fdopen(_dup(_fileno(dc->f)), "rb");
+ #else
+ f = fdopen(dup(fileno(dc->f)), "rb");
+ #endif
+ if (!f)
+ return stb__io_error(dc);
+ } else {
+ f = fopen(dc->filename, "rb");
+ free(dc->filename);
+ if (!f)
+ return stb__io_error(dc);
+ }
+
+ len = dc->len;
+ if (len < 0) {
+ fseek(f, 0, SEEK_END);
+ len = ftell(f) - dc->offset;
+ }
+
+ if (fseek(f, dc->offset, SEEK_SET)) {
+ fclose(f);
+ return stb__io_error(dc);
+ }
+
+ if (dc->output)
+ buf = dc->output;
+ else {
+ buf = (stb_uchar *) malloc(len);
+ if (buf == NULL) {
+ fclose(f);
+ return stb__io_error(dc);
+ }
+ }
+
+ len = fread(buf, 1, len, f);
+ fclose(f);
+ if (dc->len_output) *dc->len_output = len;
+ if (dc->result) *dc->result = buf;
+ if (dc->flag) *dc->flag = 1;
+
+ return NULL;
+}
+
+int stb__io_add(char *fname, FILE *f, int off, int len, stb_uchar *out, stb_uchar **result, int *olen, int *flag, stb_bgstat *stat)
+{
+ int res;
+ stb__io_init();
+ // do memory allocation outside of mutex
+ if (fname) fname = stb_p_strdup(fname);
+ stb_mutex_begin(stb__diskio_mutex);
+ {
+ stb__disk_command *dc = &stb__dc_queue[stb__dc_offset];
+ dc->filename = fname;
+ dc->f = f;
+ dc->offset = off;
+ dc->len = len;
+ dc->output = out;
+ dc->result = result;
+ dc->len_output = olen;
+ dc->flag = flag;
+ dc->stat_out = stat;
+ res = stb_workq(stb__diskio, stb__io_task, dc, NULL);
+ if (res)
+ stb__dc_offset = (stb__dc_offset + 1 == STB__MAX_DISK_COMMAND ? 0 : stb__dc_offset+1);
+ }
+ stb_mutex_end(stb__diskio_mutex);
+ return res;
+}
+
+int stb_bgio_read(char *filename, int offset, int len, stb_uchar **result, int *olen)
+{
+ return stb__io_add(filename,NULL,offset,len,NULL,result,olen,NULL,NULL);
+}
+
+int stb_bgio_readf(FILE *f, int offset, int len, stb_uchar **result, int *olen)
+{
+ return stb__io_add(NULL,f,offset,len,NULL,result,olen,NULL,NULL);
+}
+
+int stb_bgio_read_to(char *filename, int offset, int len, stb_uchar *buffer, int *olen)
+{
+ return stb__io_add(filename,NULL,offset,len,buffer,NULL,olen,NULL,NULL);
+}
+
+int stb_bgio_readf_to(FILE *f, int offset, int len, stb_uchar *buffer, int *olen)
+{
+ return stb__io_add(NULL,f,offset,len,buffer,NULL,olen,NULL,NULL);
+}
+
+STB_EXTERN int stb_bgio_stat (char *filename, stb_bgstat *result)
+{
+ result->have_data = 0;
+ return stb__io_add(filename,NULL,0,0,0,NULL,0,NULL, result);
+}
+#endif
+#endif
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Fast malloc implementation
+//
+// This is a clone of TCMalloc, but without the thread support.
+// 1. large objects are allocated directly, page-aligned
+// 2. small objects are allocated in homogeonous heaps, 0 overhead
+//
+// We keep an allocation table for pages a la TCMalloc. This would
+// require 4MB for the entire address space, but we only allocate
+// the parts that are in use. The overhead from using homogenous heaps
+// everywhere is 3MB. (That is, if you allocate 1 object of each size,
+// you'll use 3MB.)
+
+#if defined(STB_DEFINE) && ((defined(_WIN32) && !defined(_M_AMD64)) || defined(STB_FASTMALLOC))
+
+#ifdef _WIN32
+ #ifndef _WINDOWS_
+ #ifndef STB__IMPORT
+ #define STB__IMPORT STB_EXTERN __declspec(dllimport)
+ #define STB__DW unsigned long
+ #endif
+ STB__IMPORT void * __stdcall VirtualAlloc(void *p, unsigned long size, unsigned long type, unsigned long protect);
+ STB__IMPORT int __stdcall VirtualFree(void *p, unsigned long size, unsigned long freetype);
+ #endif
+ #define stb__alloc_pages_raw(x) (stb_uint32) VirtualAlloc(NULL, (x), 0x3000, 0x04)
+ #define stb__dealloc_pages_raw(p) VirtualFree((void *) p, 0, 0x8000)
+#else
+ #error "Platform not currently supported"
+#endif
+
+typedef struct stb__span
+{
+ int start, len;
+ struct stb__span *next, *prev;
+ void *first_free;
+ unsigned short list; // 1..256 free; 257..511 sizeclass; 0=large block
+ short allocations; // # outstanding allocations for sizeclass
+} stb__span; // 24
+
+static stb__span **stb__span_for_page;
+static int stb__firstpage, stb__lastpage;
+static void stb__update_page_range(int first, int last)
+{
+ stb__span **sfp;
+ int i, f,l;
+ if (first >= stb__firstpage && last <= stb__lastpage) return;
+ if (stb__span_for_page == NULL) {
+ f = first;
+ l = f+stb_max(last-f, 16384);
+ l = stb_min(l, 1<<20);
+ } else if (last > stb__lastpage) {
+ f = stb__firstpage;
+ l = f + (stb__lastpage - f) * 2;
+ l = stb_clamp(last, l,1<<20);
+ } else {
+ l = stb__lastpage;
+ f = l - (l - stb__firstpage) * 2;
+ f = stb_clamp(f, 0,first);
+ }
+ sfp = (stb__span **) stb__alloc_pages_raw(sizeof(void *) * (l-f));
+ for (i=f; i < stb__firstpage; ++i) sfp[i - f] = NULL;
+ for ( ; i < stb__lastpage ; ++i) sfp[i - f] = stb__span_for_page[i - stb__firstpage];
+ for ( ; i < l ; ++i) sfp[i - f] = NULL;
+ if (stb__span_for_page) stb__dealloc_pages_raw(stb__span_for_page);
+ stb__firstpage = f;
+ stb__lastpage = l;
+ stb__span_for_page = sfp;
+}
+
+static stb__span *stb__span_free=NULL;
+static stb__span *stb__span_first, *stb__span_end;
+static stb__span *stb__span_alloc(void)
+{
+ stb__span *s = stb__span_free;
+ if (s)
+ stb__span_free = s->next;
+ else {
+ if (!stb__span_first) {
+ stb__span_first = (stb__span *) stb__alloc_pages_raw(65536);
+ if (stb__span_first == NULL) return NULL;
+ stb__span_end = stb__span_first + (65536 / sizeof(stb__span));
+ }
+ s = stb__span_first++;
+ if (stb__span_first == stb__span_end) stb__span_first = NULL;
+ }
+ return s;
+}
+
+static stb__span *stb__spanlist[512];
+
+static void stb__spanlist_unlink(stb__span *s)
+{
+ if (s->prev)
+ s->prev->next = s->next;
+ else {
+ int n = s->list;
+ assert(stb__spanlist[n] == s);
+ stb__spanlist[n] = s->next;
+ }
+ if (s->next)
+ s->next->prev = s->prev;
+ s->next = s->prev = NULL;
+ s->list = 0;
+}
+
+static void stb__spanlist_add(int n, stb__span *s)
+{
+ s->list = n;
+ s->next = stb__spanlist[n];
+ s->prev = NULL;
+ stb__spanlist[n] = s;
+ if (s->next) s->next->prev = s;
+}
+
+#define stb__page_shift 12
+#define stb__page_size (1 << stb__page_shift)
+#define stb__page_number(x) ((x) >> stb__page_shift)
+#define stb__page_address(x) ((x) << stb__page_shift)
+
+static void stb__set_span_for_page(stb__span *s)
+{
+ int i;
+ for (i=0; i < s->len; ++i)
+ stb__span_for_page[s->start + i - stb__firstpage] = s;
+}
+
+static stb__span *stb__coalesce(stb__span *a, stb__span *b)
+{
+ assert(a->start + a->len == b->start);
+ if (a->list) stb__spanlist_unlink(a);
+ if (b->list) stb__spanlist_unlink(b);
+ a->len += b->len;
+ b->len = 0;
+ b->next = stb__span_free;
+ stb__span_free = b;
+ stb__set_span_for_page(a);
+ return a;
+}
+
+static void stb__free_span(stb__span *s)
+{
+ stb__span *n = NULL;
+ if (s->start > stb__firstpage) {
+ n = stb__span_for_page[s->start-1 - stb__firstpage];
+ if (n && n->allocations == -2 && n->start + n->len == s->start) s = stb__coalesce(n,s);
+ }
+ if (s->start + s->len < stb__lastpage) {
+ n = stb__span_for_page[s->start + s->len - stb__firstpage];
+ if (n && n->allocations == -2 && s->start + s->len == n->start) s = stb__coalesce(s,n);
+ }
+ s->allocations = -2;
+ stb__spanlist_add(s->len > 256 ? 256 : s->len, s);
+}
+
+static stb__span *stb__alloc_pages(int num)
+{
+ stb__span *s = stb__span_alloc();
+ int p;
+ if (!s) return NULL;
+ p = stb__alloc_pages_raw(num << stb__page_shift);
+ if (p == 0) { s->next = stb__span_free; stb__span_free = s; return 0; }
+ assert(stb__page_address(stb__page_number(p)) == p);
+ p = stb__page_number(p);
+ stb__update_page_range(p, p+num);
+ s->start = p;
+ s->len = num;
+ s->next = NULL;
+ s->prev = NULL;
+ stb__set_span_for_page(s);
+ return s;
+}
+
+static stb__span *stb__alloc_span(int pagecount)
+{
+ int i;
+ stb__span *p = NULL;
+ for(i=pagecount; i < 256; ++i)
+ if (stb__spanlist[i]) {
+ p = stb__spanlist[i];
+ break;
+ }
+ if (!p) {
+ p = stb__spanlist[256];
+ while (p && p->len < pagecount)
+ p = p->next;
+ }
+ if (!p) {
+ p = stb__alloc_pages(pagecount < 16 ? 16 : pagecount);
+ if (p == NULL) return 0;
+ } else
+ stb__spanlist_unlink(p);
+
+ if (p->len > pagecount) {
+ stb__span *q = stb__span_alloc();
+ if (q) {
+ q->start = p->start + pagecount;
+ q->len = p->len - pagecount;
+ p->len = pagecount;
+ for (i=0; i < q->len; ++i)
+ stb__span_for_page[q->start+i - stb__firstpage] = q;
+ stb__spanlist_add(q->len > 256 ? 256 : q->len, q);
+ }
+ }
+ return p;
+}
+
+#define STB__MAX_SMALL_SIZE 32768
+#define STB__MAX_SIZE_CLASSES 256
+
+static unsigned char stb__class_base[32];
+static unsigned char stb__class_shift[32];
+static unsigned char stb__pages_for_class[STB__MAX_SIZE_CLASSES];
+static int stb__size_for_class[STB__MAX_SIZE_CLASSES];
+
+stb__span *stb__get_nonempty_sizeclass(int c)
+{
+ int s = c + 256, i, size, tsize; // remap to span-list index
+ char *z;
+ void *q;
+ stb__span *p = stb__spanlist[s];
+ if (p) {
+ if (p->first_free) return p; // fast path: it's in the first one in list
+ for (p=p->next; p; p=p->next)
+ if (p->first_free) {
+ // move to front for future queries
+ stb__spanlist_unlink(p);
+ stb__spanlist_add(s, p);
+ return p;
+ }
+ }
+ // no non-empty ones, so allocate a new one
+ p = stb__alloc_span(stb__pages_for_class[c]);
+ if (!p) return NULL;
+ // create the free list up front
+ size = stb__size_for_class[c];
+ tsize = stb__pages_for_class[c] << stb__page_shift;
+ i = 0;
+ z = (char *) stb__page_address(p->start);
+ q = NULL;
+ while (i + size <= tsize) {
+ * (void **) z = q; q = z;
+ z += size;
+ i += size;
+ }
+ p->first_free = q;
+ p->allocations = 0;
+ stb__spanlist_add(s,p);
+ return p;
+}
+
+static int stb__sizeclass(size_t sz)
+{
+ int z = stb_log2_floor(sz); // -1 below to group e.g. 13,14,15,16 correctly
+ return stb__class_base[z] + ((sz-1) >> stb__class_shift[z]);
+}
+
+static void stb__init_sizeclass(void)
+{
+ int i, size, overhead;
+ int align_shift = 2; // allow 4-byte and 12-byte blocks as well, vs. TCMalloc
+ int next_class = 1;
+ int last_log = 0;
+
+ for (i = 0; i < align_shift; i++) {
+ stb__class_base [i] = next_class;
+ stb__class_shift[i] = align_shift;
+ }
+
+ for (size = 1 << align_shift; size <= STB__MAX_SMALL_SIZE; size += 1 << align_shift) {
+ i = stb_log2_floor(size);
+ if (i > last_log) {
+ if (size == 16) ++align_shift; // switch from 4-byte to 8-byte alignment
+ else if (size >= 128 && align_shift < 8) ++align_shift;
+ stb__class_base[i] = next_class - ((size-1) >> align_shift);
+ stb__class_shift[i] = align_shift;
+ last_log = i;
+ }
+ stb__size_for_class[next_class++] = size;
+ }
+
+ for (i=1; i <= STB__MAX_SMALL_SIZE; ++i)
+ assert(i <= stb__size_for_class[stb__sizeclass(i)]);
+
+ overhead = 0;
+ for (i = 1; i < next_class; i++) {
+ int s = stb__size_for_class[i];
+ size = stb__page_size;
+ while (size % s > size >> 3)
+ size += stb__page_size;
+ stb__pages_for_class[i] = (unsigned char) (size >> stb__page_shift);
+ overhead += size;
+ }
+ assert(overhead < (4 << 20)); // make sure it's under 4MB of overhead
+}
+
+#ifdef STB_DEBUG
+#define stb__smemset(a,b,c) memset((void *) a, b, c)
+#elif defined(STB_FASTMALLOC_INIT)
+#define stb__smemset(a,b,c) memset((void *) a, b, c)
+#else
+#define stb__smemset(a,b,c)
+#endif
+void *stb_smalloc(size_t sz)
+{
+ stb__span *s;
+ if (sz == 0) return NULL;
+ if (stb__size_for_class[1] == 0) stb__init_sizeclass();
+ if (sz > STB__MAX_SMALL_SIZE) {
+ s = stb__alloc_span((sz + stb__page_size - 1) >> stb__page_shift);
+ if (s == NULL) return NULL;
+ s->list = 0;
+ s->next = s->prev = NULL;
+ s->allocations = -32767;
+ stb__smemset(stb__page_address(s->start), 0xcd, (sz+3)&~3);
+ return (void *) stb__page_address(s->start);
+ } else {
+ void *p;
+ int c = stb__sizeclass(sz);
+ s = stb__spanlist[256+c];
+ if (!s || !s->first_free)
+ s = stb__get_nonempty_sizeclass(c);
+ if (s == NULL) return NULL;
+ p = s->first_free;
+ s->first_free = * (void **) p;
+ ++s->allocations;
+ stb__smemset(p,0xcd, sz);
+ return p;
+ }
+}
+
+int stb_ssize(void *p)
+{
+ stb__span *s;
+ if (p == NULL) return 0;
+ s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage];
+ if (s->list >= 256) {
+ return stb__size_for_class[s->list - 256];
+ } else {
+ assert(s->list == 0);
+ return s->len << stb__page_shift;
+ }
+}
+
+void stb_sfree(void *p)
+{
+ stb__span *s;
+ if (p == NULL) return;
+ s = stb__span_for_page[stb__page_number((stb_uint) p) - stb__firstpage];
+ if (s->list >= 256) {
+ stb__smemset(p, 0xfe, stb__size_for_class[s->list-256]);
+ * (void **) p = s->first_free;
+ s->first_free = p;
+ if (--s->allocations == 0) {
+ stb__spanlist_unlink(s);
+ stb__free_span(s);
+ }
+ } else {
+ assert(s->list == 0);
+ stb__smemset(p, 0xfe, stb_ssize(p));
+ stb__free_span(s);
+ }
+}
+
+void *stb_srealloc(void *p, size_t sz)
+{
+ size_t cur_size;
+ if (p == NULL) return stb_smalloc(sz);
+ if (sz == 0) { stb_sfree(p); return NULL; }
+ cur_size = stb_ssize(p);
+ if (sz > cur_size || sz <= (cur_size >> 1)) {
+ void *q;
+ if (sz > cur_size && sz < (cur_size << 1)) sz = cur_size << 1;
+ q = stb_smalloc(sz); if (q == NULL) return NULL;
+ memcpy(q, p, sz < cur_size ? sz : cur_size);
+ stb_sfree(p);
+ return q;
+ }
+ return p;
+}
+
+void *stb_scalloc(size_t n, size_t sz)
+{
+ void *p;
+ if (n == 0 || sz == 0) return NULL;
+ if (stb_log2_ceil(n) + stb_log2_ceil(n) >= 32) return NULL;
+ p = stb_smalloc(n*sz);
+ if (p) memset(p, 0, n*sz);
+ return p;
+}
+
+char *stb_sstrdup(char *s)
+{
+ int n = strlen(s);
+ char *p = (char *) stb_smalloc(n+1);
+ if (p) stb_p_strcpy_s(p,n+1,s);
+ return p;
+}
+#endif // STB_DEFINE
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Source code constants
+//
+// This is a trivial system to let you specify constants in source code,
+// then while running you can change the constants.
+//
+// Note that you can't wrap the #defines, because we need to know their
+// names. So we provide a pre-wrapped version without 'STB_' for convenience;
+// to request it, #define STB_CONVENIENT_H, yielding:
+// KI -- integer
+// KU -- unsigned integer
+// KF -- float
+// KD -- double
+// KS -- string constant
+//
+// Defaults to functioning in debug build, not in release builds.
+// To force on, define STB_ALWAYS_H
+
+#ifdef STB_CONVENIENT_H
+#define KI(x) STB_I(x)
+#define KU(x) STB_UI(x)
+#define KF(x) STB_F(x)
+#define KD(x) STB_D(x)
+#define KS(x) STB_S(x)
+#endif
+
+STB_EXTERN void stb_source_path(char *str);
+#ifdef STB_DEFINE
+char *stb__source_path;
+void stb_source_path(char *path)
+{
+ stb__source_path = path;
+}
+
+char *stb__get_sourcefile_path(char *file)
+{
+ static char filebuf[512];
+ if (stb__source_path) {
+ stb_p_sprintf(filebuf stb_p_size(sizeof(filebuf)), "%s/%s", stb__source_path, file);
+ if (stb_fexists(filebuf)) return filebuf;
+ }
+
+ if (stb_fexists(file)) return file;
+
+ stb_p_sprintf(filebuf stb_p_size(sizeof(filebuf)), "../%s", file);
+ if (!stb_fexists(filebuf)) return filebuf;
+
+ return file;
+}
+#endif
+
+#define STB_F(x) ((float) STB_H(x))
+#define STB_UI(x) ((unsigned int) STB_I(x))
+
+#if !defined(STB_DEBUG) && !defined(STB_ALWAYS_H)
+#define STB_D(x) ((double) (x))
+#define STB_I(x) ((int) (x))
+#define STB_S(x) ((char *) (x))
+#else
+#define STB_D(x) stb__double_constant(__FILE__, __LINE__-1, (x))
+#define STB_I(x) stb__int_constant(__FILE__, __LINE__-1, (x))
+#define STB_S(x) stb__string_constant(__FILE__, __LINE__-1, (x))
+
+STB_EXTERN double stb__double_constant(char *file, int line, double x);
+STB_EXTERN int stb__int_constant(char *file, int line, int x);
+STB_EXTERN char * stb__string_constant(char *file, int line, char *str);
+
+#ifdef STB_DEFINE
+
+enum
+{
+ STB__CTYPE_int,
+ STB__CTYPE_uint,
+ STB__CTYPE_float,
+ STB__CTYPE_double,
+ STB__CTYPE_string,
+};
+
+typedef struct
+{
+ int line;
+ int type;
+ union {
+ int ival;
+ double dval;
+ char *sval;
+ };
+} stb__Entry;
+
+typedef struct
+{
+ stb__Entry *entries;
+ char *filename;
+ time_t timestamp;
+ char **file_data;
+ int file_len;
+ unsigned short *line_index;
+} stb__FileEntry;
+
+static void stb__constant_parse(stb__FileEntry *f, int i)
+{
+ char *s;
+ int n;
+ if (!stb_arr_valid(f->entries, i)) return;
+ n = f->entries[i].line;
+ if (n >= f->file_len) return;
+ s = f->file_data[n];
+ switch (f->entries[i].type) {
+ case STB__CTYPE_float:
+ while (*s) {
+ if (!strncmp(s, "STB_D(", 6)) { s+=6; goto matched_float; }
+ if (!strncmp(s, "STB_F(", 6)) { s+=6; goto matched_float; }
+ if (!strncmp(s, "KD(", 3)) { s+=3; goto matched_float; }
+ if (!strncmp(s, "KF(", 3)) { s+=3; goto matched_float; }
+ ++s;
+ }
+ break;
+ matched_float:
+ f->entries[i].dval = strtod(s, NULL);
+ break;
+ case STB__CTYPE_int:
+ while (*s) {
+ if (!strncmp(s, "STB_I(", 6)) { s+=6; goto matched_int; }
+ if (!strncmp(s, "STB_UI(", 7)) { s+=7; goto matched_int; }
+ if (!strncmp(s, "KI(", 3)) { s+=3; goto matched_int; }
+ if (!strncmp(s, "KU(", 3)) { s+=3; goto matched_int; }
+ ++s;
+ }
+ break;
+ matched_int: {
+ int neg=0;
+ s = stb_skipwhite(s);
+ while (*s == '-') { neg = !neg; s = stb_skipwhite(s+1); } // handle '- - 5', pointlessly
+ if (s[0] == '0' && tolower(s[1]) == 'x')
+ f->entries[i].ival = strtol(s, NULL, 16);
+ else if (s[0] == '0')
+ f->entries[i].ival = strtol(s, NULL, 8);
+ else
+ f->entries[i].ival = strtol(s, NULL, 10);
+ if (neg) f->entries[i].ival = -f->entries[i].ival;
+ break;
+ }
+ case STB__CTYPE_string:
+ // @TODO
+ break;
+ }
+}
+
+static stb_sdict *stb__constant_file_hash;
+
+stb__Entry *stb__constant_get_entry(char *filename, int line, int type)
+{
+ int i;
+ stb__FileEntry *f;
+ if (stb__constant_file_hash == NULL)
+ stb__constant_file_hash = stb_sdict_new(STB_TRUE);
+ f = (stb__FileEntry*) stb_sdict_get(stb__constant_file_hash, filename);
+ if (f == NULL) {
+ char *s = stb__get_sourcefile_path(filename);
+ if (s == NULL || !stb_fexists(s)) return 0;
+ f = (stb__FileEntry *) malloc(sizeof(*f));
+ f->timestamp = stb_ftimestamp(s);
+ f->file_data = stb_stringfile(s, &f->file_len);
+ f->filename = stb_p_strdup(s); // cache the full path
+ f->entries = NULL;
+ f->line_index = 0;
+ stb_arr_setlen(f->line_index, f->file_len);
+ memset(f->line_index, 0xff, stb_arr_storage(f->line_index));
+ } else {
+ time_t t = stb_ftimestamp(f->filename);
+ if (f->timestamp != t) {
+ f->timestamp = t;
+ free(f->file_data);
+ f->file_data = stb_stringfile(f->filename, &f->file_len);
+ stb_arr_setlen(f->line_index, f->file_len);
+ for (i=0; i < stb_arr_len(f->entries); ++i)
+ stb__constant_parse(f, i);
+ }
+ }
+
+ if (line >= f->file_len) return 0;
+
+ if (f->line_index[line] >= stb_arr_len(f->entries)) {
+ // need a new entry
+ int n = stb_arr_len(f->entries);
+ stb__Entry e;
+ e.line = line;
+ if (line < f->file_len)
+ f->line_index[line] = n;
+ e.type = type;
+ stb_arr_push(f->entries, e);
+ stb__constant_parse(f, n);
+ }
+ return f->entries + f->line_index[line];
+}
+
+double stb__double_constant(char *file, int line, double x)
+{
+ stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_float);
+ if (!e) return x;
+ return e->dval;
+}
+
+int stb__int_constant(char *file, int line, int x)
+{
+ stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_int);
+ if (!e) return x;
+ return e->ival;
+}
+
+char * stb__string_constant(char *file, int line, char *x)
+{
+ stb__Entry *e = stb__constant_get_entry(file, line, STB__CTYPE_string);
+ if (!e) return x;
+ return e->sval;
+}
+
+#endif // STB_DEFINE
+#endif // !STB_DEBUG && !STB_ALWAYS_H
+
+
+#ifdef STB_STUA
+#error "STUA is no longer supported"
+//////////////////////////////////////////////////////////////////////////
+//
+// stua: little scripting language
+//
+// define STB_STUA to compile it
+//
+// see http://nothings.org/stb/stb_stua.html for documentation
+//
+// basic parsing model:
+//
+// lexical analysis
+// use stb_lex() to parse tokens; keywords get their own tokens
+//
+// parsing:
+// recursive descent parser. too much of a hassle to make an unambiguous
+// LR(1) grammar, and one-pass generation is clumsier (recursive descent
+// makes it easier to e.g. compile nested functions). on the other hand,
+// dictionary syntax required hackery to get extra lookahead.
+//
+// codegen:
+// output into an evaluation tree, using array indices as 'pointers'
+//
+// run:
+// traverse the tree; support for 'break/continue/return' is tricky
+//
+// garbage collection:
+// stu__mark and sweep; explicit stack with non-stu__compile_global_scope roots
+
+typedef stb_int32 stua_obj;
+
+typedef stb_idict stua_dict;
+
+STB_EXTERN void stua_run_script(char *s);
+STB_EXTERN void stua_uninit(void);
+
+extern stua_obj stua_globals;
+
+STB_EXTERN double stua_number(stua_obj z);
+
+STB_EXTERN stua_obj stua_getnil(void);
+STB_EXTERN stua_obj stua_getfalse(void);
+STB_EXTERN stua_obj stua_gettrue(void);
+STB_EXTERN stua_obj stua_string(char *z);
+STB_EXTERN stua_obj stua_make_number(double d);
+STB_EXTERN stua_obj stua_box(int type, void *data, int size);
+
+enum
+{
+ STUA_op_negate=129,
+ STUA_op_shl, STUA_op_ge,
+ STUA_op_shr, STUA_op_le,
+ STUA_op_shru,
+ STUA_op_last
+};
+
+#define STUA_NO_VALUE 2 // equivalent to a tagged NULL
+STB_EXTERN stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c);
+
+STB_EXTERN stua_obj stua_error(char *err, ...);
+
+STB_EXTERN stua_obj stua_pushroot(stua_obj o);
+STB_EXTERN void stua_poproot ( void );
+
+
+#ifdef STB_DEFINE
+// INTERPRETER
+
+// 31-bit floating point implementation
+// force the (1 << 30) bit (2nd highest bit) to be zero by re-biasing the exponent;
+// then shift and set the bottom bit
+
+static stua_obj stu__floatp(float *f)
+{
+ unsigned int n = *(unsigned int *) f;
+ unsigned int e = n & (0xff << 23);
+
+ assert(sizeof(int) == 4 && sizeof(float) == 4);
+
+ if (!e) // zero?
+ n = n; // no change
+ else if (e < (64 << 23)) // underflow of the packed encoding?
+ n = (n & 0x80000000); // signed 0
+ else if (e > (190 << 23)) // overflow of the encoding? (or INF or NAN)
+ n = (n & 0x80000000) + (127 << 23); // new INF encoding
+ else
+ n -= 0x20000000;
+
+ // now we need to shuffle the bits so that the spare bit is at the bottom
+ assert((n & 0x40000000) == 0);
+ return (n & 0x80000000) + (n << 1) + 1;
+}
+
+static unsigned char stu__getfloat_addend[256];
+static float stu__getfloat(stua_obj v)
+{
+ unsigned int n;
+ unsigned int e = ((unsigned int) v) >> 24;
+
+ n = (int) v >> 1; // preserve high bit
+ n += stu__getfloat_addend[e] << 24;
+ return *(float *) &n;
+}
+
+stua_obj stua_float(float f)
+{
+ return stu__floatp(&f);
+}
+
+static void stu__float_init(void)
+{
+ int i;
+ stu__getfloat_addend[0] = 0; // do nothing to biased exponent of 0
+ for (i=1; i < 127; ++i)
+ stu__getfloat_addend[i] = 32; // undo the -0x20000000
+ stu__getfloat_addend[127] = 64; // convert packed INF to INF (0x3f -> 0x7f)
+
+ for (i=0; i < 128; ++i) // for signed floats, remove the bit we just shifted down
+ stu__getfloat_addend[128+i] = stu__getfloat_addend[i] - 64;
+}
+
+// Tagged data type implementation
+
+ // TAGS:
+#define stu__int_tag 0 // of 2 bits // 00 int
+#define stu__float_tag 1 // of 1 bit // 01 float
+#define stu__ptr_tag 2 // of 2 bits // 10 boxed
+ // 11 float
+
+#define stu__tag(x) ((x) & 3)
+#define stu__number(x) (stu__tag(x) != stu__ptr_tag)
+#define stu__isint(x) (stu__tag(x) == stu__int_tag)
+
+#define stu__int(x) ((x) >> 2)
+#define stu__float(x) (stu__getfloat(x))
+
+#define stu__makeint(v) ((v)*4+stu__int_tag)
+
+// boxed data, and tag support for boxed data
+
+enum
+{
+ STU___float = 1, STU___int = 2,
+ STU___number = 3, STU___string = 4,
+ STU___function = 5, STU___dict = 6,
+ STU___boolean = 7, STU___error = 8,
+};
+
+// boxed data
+#define STU__BOX short type, stua_gc
+typedef struct stu__box { STU__BOX; } stu__box;
+
+stu__box stu__nil = { 0, 1 };
+stu__box stu__true = { STU___boolean, 1, };
+stu__box stu__false = { STU___boolean, 1, };
+
+#define stu__makeptr(v) ((stua_obj) (v) + stu__ptr_tag)
+
+#define stua_nil stu__makeptr(&stu__nil)
+#define stua_true stu__makeptr(&stu__true)
+#define stua_false stu__makeptr(&stu__false)
+
+stua_obj stua_getnil(void) { return stua_nil; }
+stua_obj stua_getfalse(void) { return stua_false; }
+stua_obj stua_gettrue(void) { return stua_true; }
+
+#define stu__ptr(x) ((stu__box *) ((x) - stu__ptr_tag))
+
+#define stu__checkt(t,x) ((t) == STU___float ? ((x) & 1) == stu__float_tag : \
+ (t) == STU___int ? stu__isint(x) : \
+ (t) == STU___number ? stu__number(x) : \
+ stu__tag(x) == stu__ptr_tag && stu__ptr(x)->type == (t))
+
+typedef struct
+{
+ STU__BOX;
+ void *ptr;
+} stu__wrapper;
+
+// implementation of a 'function' or function + closure
+
+typedef struct stu__func
+{
+ STU__BOX;
+ stua_obj closure_source; // 0 - regular function; 4 - C function
+ // if closure, pointer to source function
+ union {
+ stua_obj closure_data; // partial-application data
+ void *store; // pointer to free that holds 'code'
+ stua_obj (*func)(stua_dict *context);
+ } f;
+ // closure ends here
+ short *code;
+ int num_param;
+ stua_obj *param; // list of parameter strings
+} stu__func;
+
+// apply this to 'short *code' to get at data
+#define stu__const(f) ((stua_obj *) (f))
+
+static void stu__free_func(stu__func *f)
+{
+ if (f->closure_source == 0) free(f->f.store);
+ if ((stb_uint) f->closure_source <= 4) free(f->param);
+ free(f);
+}
+
+#define stu__pd(x) ((stua_dict *) stu__ptr(x))
+#define stu__pw(x) ((stu__wrapper *) stu__ptr(x))
+#define stu__pf(x) ((stu__func *) stu__ptr(x))
+
+
+// garbage-collection
+
+
+static stu__box ** stu__gc_ptrlist;
+static stua_obj * stu__gc_root_stack;
+
+stua_obj stua_pushroot(stua_obj o) { stb_arr_push(stu__gc_root_stack, o); return o; }
+void stua_poproot ( void ) { stb_arr_pop(stu__gc_root_stack); }
+
+static stb_sdict *stu__strings;
+static void stu__mark(stua_obj z)
+{
+ int i;
+ stu__box *p = stu__ptr(z);
+ if (p->stua_gc == 1) return; // already marked
+ assert(p->stua_gc == 0);
+ p->stua_gc = 1;
+ switch(p->type) {
+ case STU___function: {
+ stu__func *f = (stu__func *) p;
+ if ((stb_uint) f->closure_source <= 4) {
+ if (f->closure_source == 0) {
+ for (i=1; i <= f->code[0]; ++i)
+ if (!stu__number(((stua_obj *) f->code)[-i]))
+ stu__mark(((stua_obj *) f->code)[-i]);
+ }
+ for (i=0; i < f->num_param; ++i)
+ stu__mark(f->param[i]);
+ } else {
+ stu__mark(f->closure_source);
+ stu__mark(f->f.closure_data);
+ }
+ break;
+ }
+ case STU___dict: {
+ stua_dict *e = (stua_dict *) p;
+ for (i=0; i < e->limit; ++i)
+ if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL) {
+ if (!stu__number(e->table[i].k)) stu__mark((int) e->table[i].k);
+ if (!stu__number(e->table[i].v)) stu__mark((int) e->table[i].v);
+ }
+ break;
+ }
+ }
+}
+
+static int stu__num_allocs, stu__size_allocs;
+static stua_obj stu__flow_val = stua_nil; // used for break & return
+
+static void stua_gc(int force)
+{
+ int i;
+ if (!force && stu__num_allocs == 0 && stu__size_allocs == 0) return;
+ stu__num_allocs = stu__size_allocs = 0;
+ //printf("[gc]\n");
+
+ // clear marks
+ for (i=0; i < stb_arr_len(stu__gc_ptrlist); ++i)
+ stu__gc_ptrlist[i]->stua_gc = 0;
+
+ // stu__mark everything reachable
+ stu__nil.stua_gc = stu__true.stua_gc = stu__false.stua_gc = 1;
+ stu__mark(stua_globals);
+ if (!stu__number(stu__flow_val))
+ stu__mark(stu__flow_val);
+ for (i=0; i < stb_arr_len(stu__gc_root_stack); ++i)
+ if (!stu__number(stu__gc_root_stack[i]))
+ stu__mark(stu__gc_root_stack[i]);
+
+ // sweep unreachables
+ for (i=0; i < stb_arr_len(stu__gc_ptrlist);) {
+ stu__box *z = stu__gc_ptrlist[i];
+ if (!z->stua_gc) {
+ switch (z->type) {
+ case STU___dict: stb_idict_destroy((stua_dict *) z); break;
+ case STU___error: free(((stu__wrapper *) z)->ptr); break;
+ case STU___string: stb_sdict_remove(stu__strings, (char*) ((stu__wrapper *) z)->ptr, NULL); free(z); break;
+ case STU___function: stu__free_func((stu__func *) z); break;
+ }
+ // swap in the last item over this, and repeat
+ z = stb_arr_pop(stu__gc_ptrlist);
+ stu__gc_ptrlist[i] = z;
+ } else
+ ++i;
+ }
+}
+
+static void stu__consider_gc(stua_obj x)
+{
+ if (stu__size_allocs < 100000) return;
+ if (stu__num_allocs < 10 && stu__size_allocs < 1000000) return;
+ stb_arr_push(stu__gc_root_stack, x);
+ stua_gc(0);
+ stb_arr_pop(stu__gc_root_stack);
+}
+
+static stua_obj stu__makeobj(int type, void *data, int size, int safe_to_gc)
+{
+ stua_obj x = stu__makeptr(data);
+ ((stu__box *) data)->type = type;
+ stb_arr_push(stu__gc_ptrlist, (stu__box *) data);
+ stu__num_allocs += 1;
+ stu__size_allocs += size;
+ if (safe_to_gc) stu__consider_gc(x);
+ return x;
+}
+
+stua_obj stua_box(int type, void *data, int size)
+{
+ stu__wrapper *p = (stu__wrapper *) malloc(sizeof(*p));
+ p->ptr = data;
+ return stu__makeobj(type, p, size, 0);
+}
+
+// a stu string can be directly compared for equality, because
+// they go into a hash table
+stua_obj stua_string(char *z)
+{
+ stu__wrapper *b = (stu__wrapper *) stb_sdict_get(stu__strings, z);
+ if (b == NULL) {
+ int o = stua_box(STU___string, NULL, strlen(z) + sizeof(*b));
+ b = stu__pw(o);
+ stb_sdict_add(stu__strings, z, b);
+ stb_sdict_getkey(stu__strings, z, (char **) &b->ptr);
+ }
+ return stu__makeptr(b);
+}
+
+// stb_obj dictionary is just an stb_idict
+static void stu__set(stua_dict *d, stua_obj k, stua_obj v)
+{ if (stb_idict_set(d, k, v)) stu__size_allocs += 8; }
+
+static stua_obj stu__get(stua_dict *d, stua_obj k, stua_obj res)
+{
+ stb_idict_get_flag(d, k, &res);
+ return res;
+}
+
+static stua_obj make_string(char *z, int len)
+{
+ stua_obj s;
+ char temp[256], *q = (char *) stb_temp(temp, len+1), *p = q;
+ while (len > 0) {
+ if (*z == '\\') {
+ if (z[1] == 'n') *p = '\n';
+ else if (z[1] == 'r') *p = '\r';
+ else if (z[1] == 't') *p = '\t';
+ else *p = z[1];
+ p += 1; z += 2; len -= 2;
+ } else {
+ *p++ = *z++; len -= 1;
+ }
+ }
+ *p = 0;
+ s = stua_string(q);
+ stb_tempfree(temp, q);
+ return s;
+}
+
+enum token_names
+{
+ T__none=128,
+ ST_shl = STUA_op_shl, ST_ge = STUA_op_ge,
+ ST_shr = STUA_op_shr, ST_le = STUA_op_le,
+ ST_shru = STUA_op_shru, STU__negate = STUA_op_negate,
+ ST__reset_numbering = STUA_op_last,
+ ST_white,
+ ST_id, ST_float, ST_decimal, ST_hex, ST_char,ST_string, ST_number,
+ // make sure the keywords come _AFTER_ ST_id, so stb_lex prefer them
+ ST_if, ST_while, ST_for, ST_eq, ST_nil,
+ ST_then, ST_do, ST_in, ST_ne, ST_true,
+ ST_else, ST_break, ST_let, ST_and, ST_false,
+ ST_elseif, ST_continue, ST_into, ST_or, ST_repeat,
+ ST_end, ST_as, ST_return, ST_var, ST_func,
+ ST_catch, ST__frame,
+ ST__max_terminals,
+
+ STU__defaultparm, STU__seq,
+};
+
+static stua_dict * stu__globaldict;
+ stua_obj stua_globals;
+
+static enum
+{
+ FLOW_normal, FLOW_continue, FLOW_break, FLOW_return, FLOW_error,
+} stu__flow;
+
+stua_obj stua_error(char *z, ...)
+{
+ stua_obj a;
+ char temp[4096], *x;
+ va_list v; va_start(v,z); vsprintf(temp, z, v); va_end(v);
+ x = stb_p_strdup(temp);
+ a = stua_box(STU___error, x, strlen(x));
+ stu__flow = FLOW_error;
+ stu__flow_val = a;
+ return stua_nil;
+}
+
+double stua_number(stua_obj z)
+{
+ return stu__tag(z) == stu__int_tag ? stu__int(z) : stu__float(z);
+}
+
+stua_obj stua_make_number(double d)
+{
+ double e = floor(d);
+ if (e == d && e < (1 << 29) && e >= -(1 << 29))
+ return stu__makeint((int) e);
+ else
+ return stua_float((float) d);
+}
+
+stua_obj (*stua_overload)(int op, stua_obj a, stua_obj b, stua_obj c) = NULL;
+
+static stua_obj stu__op(int op, stua_obj a, stua_obj b, stua_obj c)
+{
+ stua_obj r = STUA_NO_VALUE;
+ if (op == '+') {
+ if (stu__checkt(STU___string, a) && stu__checkt(STU___string, b)) {
+ ;// @TODO: string concatenation
+ } else if (stu__checkt(STU___function, a) && stu__checkt(STU___dict, b)) {
+ stu__func *f = (stu__func *) malloc(12);
+ assert(offsetof(stu__func, code)==12);
+ f->closure_source = a;
+ f->f.closure_data = b;
+ return stu__makeobj(STU___function, f, 16, 1);
+ }
+ }
+ if (stua_overload) r = stua_overload(op,a,b,c);
+ if (stu__flow != FLOW_error && r == STUA_NO_VALUE)
+ stua_error("Typecheck for operator %d", op), r=stua_nil;
+ return r;
+}
+
+#define STU__EVAL2(a,b) \
+ a = stu__eval(stu__f[n+1]); if (stu__flow) break; stua_pushroot(a); \
+ b = stu__eval(stu__f[n+2]); stua_poproot(); if (stu__flow) break;
+
+#define STU__FB(op) \
+ STU__EVAL2(a,b) \
+ if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \
+ return ((a) op (b)); \
+ if (stu__number(a) && stu__number(b)) \
+ return stua_make_number(stua_number(a) op stua_number(b)); \
+ return stu__op(stu__f[n], a,b, stua_nil)
+
+#define STU__F(op) \
+ STU__EVAL2(a,b) \
+ if (stu__number(a) && stu__number(b)) \
+ return stua_make_number(stua_number(a) op stua_number(b)); \
+ return stu__op(stu__f[n], a,b, stua_nil)
+
+#define STU__I(op) \
+ STU__EVAL2(a,b) \
+ if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag) \
+ return stu__makeint(stu__int(a) op stu__int(b)); \
+ return stu__op(stu__f[n], a,b, stua_nil)
+
+#define STU__C(op) \
+ STU__EVAL2(a,b) \
+ if (stu__number(a) && stu__number(b)) \
+ return (stua_number(a) op stua_number(b)) ? stua_true : stua_false; \
+ return stu__op(stu__f[n], a,b, stua_nil)
+
+#define STU__CE(op) \
+ STU__EVAL2(a,b) \
+ return (a op b) ? stua_true : stua_false
+
+static short *stu__f;
+static stua_obj stu__f_obj;
+static stua_dict *stu__c;
+static stua_obj stu__funceval(stua_obj fo, stua_obj co);
+
+static int stu__cond(stua_obj x)
+{
+ if (stu__flow) return 0;
+ if (!stu__checkt(STU___boolean, x))
+ x = stu__op('!', x, stua_nil, stua_nil);
+ if (x == stua_true ) return 1;
+ if (x == stua_false) return 0;
+ stu__flow = FLOW_error;
+ return 0;
+}
+
+// had to manually eliminate tailcall recursion for debugging complex stuff
+#define TAILCALL(x) n = (x); goto top;
+static stua_obj stu__eval(int n)
+{
+top:
+ if (stu__flow >= FLOW_return) return stua_nil; // is this needed?
+ if (n < 0) return stu__const(stu__f)[n];
+ assert(n != 0 && n != 1);
+ switch (stu__f[n]) {
+ stua_obj a,b,c;
+ case ST_catch: a = stu__eval(stu__f[n+1]);
+ if (stu__flow == FLOW_error) { a=stu__flow_val; stu__flow = FLOW_normal; }
+ return a;
+ case ST_var: b = stu__eval(stu__f[n+2]); if (stu__flow) break;
+ stu__set(stu__c, stu__const(stu__f)[stu__f[n+1]], b);
+ return b;
+ case STU__seq: stu__eval(stu__f[n+1]); if (stu__flow) break;
+ TAILCALL(stu__f[n+2]);
+ case ST_if: if (!stu__cond(stu__eval(stu__f[n+1]))) return stua_nil;
+ TAILCALL(stu__f[n+2]);
+ case ST_else: a = stu__cond(stu__eval(stu__f[n+1]));
+ TAILCALL(stu__f[n + 2 + !a]);
+ #define STU__HANDLE_BREAK \
+ if (stu__flow >= FLOW_break) { \
+ if (stu__flow == FLOW_break) { \
+ a = stu__flow_val; \
+ stu__flow = FLOW_normal; \
+ stu__flow_val = stua_nil; \
+ return a; \
+ } \
+ return stua_nil; \
+ }
+ case ST_as: stu__eval(stu__f[n+3]);
+ STU__HANDLE_BREAK
+ // fallthrough!
+ case ST_while: a = stua_nil; stua_pushroot(a);
+ while (stu__cond(stu__eval(stu__f[n+1]))) {
+ stua_poproot();
+ a = stu__eval(stu__f[n+2]);
+ STU__HANDLE_BREAK
+ stu__flow = FLOW_normal; // clear 'continue' flag
+ stua_pushroot(a);
+ if (stu__f[n+3]) stu__eval(stu__f[n+3]);
+ STU__HANDLE_BREAK
+ stu__flow = FLOW_normal; // clear 'continue' flag
+ }
+ stua_poproot();
+ return a;
+ case ST_break: stu__flow = FLOW_break; stu__flow_val = stu__eval(stu__f[n+1]); break;
+ case ST_continue:stu__flow = FLOW_continue; break;
+ case ST_return: stu__flow = FLOW_return; stu__flow_val = stu__eval(stu__f[n+1]); break;
+ case ST__frame: return stu__f_obj;
+ case '[': STU__EVAL2(a,b);
+ if (stu__checkt(STU___dict, a))
+ return stu__get(stu__pd(a), b, stua_nil);
+ return stu__op(stu__f[n], a, b, stua_nil);
+ case '=': a = stu__eval(stu__f[n+2]); if (stu__flow) break;
+ n = stu__f[n+1];
+ if (stu__f[n] == ST_id) {
+ if (!stb_idict_update(stu__c, stu__const(stu__f)[stu__f[n+1]], a))
+ if (!stb_idict_update(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], a))
+ return stua_error("Assignment to undefined variable");
+ } else if (stu__f[n] == '[') {
+ stua_pushroot(a);
+ b = stu__eval(stu__f[n+1]); if (stu__flow) { stua_poproot(); break; }
+ stua_pushroot(b);
+ c = stu__eval(stu__f[n+2]); stua_poproot(); stua_poproot();
+ if (stu__flow) break;
+ if (!stu__checkt(STU___dict, b)) return stua_nil;
+ stu__set(stu__pd(b), c, a);
+ } else {
+ return stu__op(stu__f[n], stu__eval(n), a, stua_nil);
+ }
+ return a;
+ case STU__defaultparm:
+ a = stu__eval(stu__f[n+2]);
+ stu__flow = FLOW_normal;
+ if (stb_idict_add(stu__c, stu__const(stu__f)[stu__f[n+1]], a))
+ stu__size_allocs += 8;
+ return stua_nil;
+ case ST_id: a = stu__get(stu__c, stu__const(stu__f)[stu__f[n+1]], STUA_NO_VALUE); // try local variable
+ return a != STUA_NO_VALUE // else try stu__compile_global_scope variable
+ ? a : stu__get(stu__globaldict, stu__const(stu__f)[stu__f[n+1]], stua_nil);
+ case STU__negate:a = stu__eval(stu__f[n+1]); if (stu__flow) break;
+ return stu__isint(a) ? -a : stu__op(stu__f[n], a, stua_nil, stua_nil);
+ case '~': a = stu__eval(stu__f[n+1]); if (stu__flow) break;
+ return stu__isint(a) ? (~a)&~3 : stu__op(stu__f[n], a, stua_nil, stua_nil);
+ case '!': a = stu__eval(stu__f[n+1]); if (stu__flow) break;
+ a = stu__cond(a); if (stu__flow) break;
+ return a ? stua_true : stua_false;
+ case ST_eq: STU__CE(==); case ST_le: STU__C(<=); case '<': STU__C(<);
+ case ST_ne: STU__CE(!=); case ST_ge: STU__C(>=); case '>': STU__C(>);
+ case '+' : STU__FB(+); case '*': STU__F(*); case '&': STU__I(&); case ST_shl: STU__I(<<);
+ case '-' : STU__FB(-); case '/': STU__F(/); case '|': STU__I(|); case ST_shr: STU__I(>>);
+ case '%': STU__I(%); case '^': STU__I(^);
+ case ST_shru: STU__EVAL2(a,b);
+ if (stu__tag(a) == stu__int_tag && stu__tag(b) == stu__int_tag)
+ return stu__makeint((unsigned) stu__int(a) >> stu__int(b));
+ return stu__op(stu__f[n], a,b, stua_nil);
+ case ST_and: a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break;
+ return a ? stu__eval(stu__f[n+2]) : a;
+ case ST_or : a = stu__eval(stu__f[n+1]); b = stu__cond(a); if (stu__flow) break;
+ return a ? b : stu__eval(stu__f[n+2]);
+ case'(':case':': STU__EVAL2(a,b);
+ if (!stu__checkt(STU___function, a))
+ return stu__op(stu__f[n], a,b, stua_nil);
+ if (!stu__checkt(STU___dict, b))
+ return stua_nil;
+ if (stu__f[n] == ':')
+ b = stu__makeobj(STU___dict, stb_idict_copy(stu__pd(b)), stb_idict_memory_usage(stu__pd(b)), 0);
+ a = stu__funceval(a,b);
+ return a;
+ case '{' : {
+ stua_dict *d;
+ d = stb_idict_new_size(stu__f[n+1] > 40 ? 64 : 16);
+ if (d == NULL)
+ return stua_nil; // breakpoint fodder
+ c = stu__makeobj(STU___dict, d, 32, 1);
+ stua_pushroot(c);
+ a = stu__f[n+1];
+ for (b=0; b < a; ++b) {
+ stua_obj x = stua_pushroot(stu__eval(stu__f[n+2 + b*2 + 0]));
+ stua_obj y = stu__eval(stu__f[n+2 + b*2 + 1]);
+ stua_poproot();
+ if (stu__flow) { stua_poproot(); return stua_nil; }
+ stu__set(d, x, y);
+ }
+ stua_poproot();
+ return c;
+ }
+ default: if (stu__f[n] < 0) return stu__const(stu__f)[stu__f[n]];
+ assert(0); /* NOTREACHED */ // internal error!
+ }
+ return stua_nil;
+}
+
+int stb__stua_nesting;
+static stua_obj stu__funceval(stua_obj fo, stua_obj co)
+{
+ stu__func *f = stu__pf(fo);
+ stua_dict *context = stu__pd(co);
+ int i,j;
+ stua_obj p;
+ short *tf = stu__f; // save previous function
+ stua_dict *tc = stu__c;
+
+ if (stu__flow == FLOW_error) return stua_nil;
+ assert(stu__flow == FLOW_normal);
+
+ stua_pushroot(fo);
+ stua_pushroot(co);
+ stu__consider_gc(stua_nil);
+
+ while ((stb_uint) f->closure_source > 4) {
+ // add data from closure to context
+ stua_dict *e = (stua_dict *) stu__pd(f->f.closure_data);
+ for (i=0; i < e->limit; ++i)
+ if (e->table[i].k != STB_IEMPTY && e->table[i].k != STB_IDEL)
+ if (stb_idict_add(context, e->table[i].k, e->table[i].v))
+ stu__size_allocs += 8;
+ // use add so if it's already defined, we don't override it; that way
+ // explicit parameters win over applied ones, and most recent applications
+ // win over previous ones
+ f = stu__pf(f->closure_source);
+ }
+
+ for (j=0, i=0; i < f->num_param; ++i)
+ // if it doesn't already exist, add it from the numbered parameters
+ if (stb_idict_add(context, f->param[i], stu__get(context, stu__int(j), stua_nil)))
+ ++j;
+
+ // @TODO: if (stu__get(context, stu__int(f->num_param+1)) != STUA_NO_VALUE) // error: too many parameters
+ // @TODO: ditto too few parameters
+
+ if (f->closure_source == 4)
+ p = f->f.func(context);
+ else {
+ stu__f = f->code, stu__c = context;
+ stu__f_obj = co;
+ ++stb__stua_nesting;
+ if (stu__f[1])
+ p = stu__eval(stu__f[1]);
+ else
+ p = stua_nil;
+ --stb__stua_nesting;
+ stu__f = tf, stu__c = tc; // restore previous function
+ if (stu__flow == FLOW_return) {
+ stu__flow = FLOW_normal;
+ p = stu__flow_val;
+ stu__flow_val = stua_nil;
+ }
+ }
+
+ stua_poproot();
+ stua_poproot();
+
+ return p;
+}
+
+// Parser
+
+static int stu__tok;
+static stua_obj stu__tokval;
+
+static char *stu__curbuf, *stu__bufstart;
+
+static stb_matcher *stu__lex_matcher;
+
+static unsigned char stu__prec[ST__max_terminals], stu__end[ST__max_terminals];
+
+static void stu__nexttoken(void)
+{
+ int len;
+
+retry:
+ stu__tok = stb_lex(stu__lex_matcher, stu__curbuf, &len);
+ if (stu__tok == 0)
+ return;
+ switch(stu__tok) {
+ case ST_white : stu__curbuf += len; goto retry;
+ case T__none : stu__tok = *stu__curbuf; break;
+ case ST_string: stu__tokval = make_string(stu__curbuf+1, len-2); break;
+ case ST_id : stu__tokval = make_string(stu__curbuf, len); break;
+ case ST_hex : stu__tokval = stu__makeint(strtol(stu__curbuf+2,NULL,16)); stu__tok = ST_number; break;
+ case ST_decimal: stu__tokval = stu__makeint(strtol(stu__curbuf ,NULL,10)); stu__tok = ST_number; break;
+ case ST_float : stu__tokval = stua_float((float) atof(stu__curbuf)) ; stu__tok = ST_number; break;
+ case ST_char : stu__tokval = stu__curbuf[2] == '\\' ? stu__curbuf[3] : stu__curbuf[2];
+ if (stu__curbuf[3] == 't') stu__tokval = '\t';
+ if (stu__curbuf[3] == 'n') stu__tokval = '\n';
+ if (stu__curbuf[3] == 'r') stu__tokval = '\r';
+ stu__tokval = stu__makeint(stu__tokval);
+ stu__tok = ST_number;
+ break;
+ }
+ stu__curbuf += len;
+}
+
+static struct { int stu__tok; char *regex; } stu__lexemes[] =
+{
+ ST_white , "([ \t\n\r]|/\\*(.|\n)*\\*/|//[^\r\n]*([\r\n]|$))+",
+ ST_id , "[_a-zA-Z][_a-zA-Z0-9]*",
+ ST_hex , "0x[0-9a-fA-F]+",
+ ST_decimal, "[0-9]+[0-9]*",
+ ST_float , "[0-9]+\\.?[0-9]*([eE][-+]?[0-9]+)?",
+ ST_float , "\\.[0-9]+([eE][-+]?[0-9]+)?",
+ ST_char , "c'(\\\\.|[^\\'])'",
+ ST_string , "\"(\\\\.|[^\\\"\n\r])*\"",
+ ST_string , "\'(\\\\.|[^\\\'\n\r])*\'",
+
+ #define stua_key4(a,b,c,d) ST_##a, #a, ST_##b, #b, ST_##c, #c, ST_##d, #d,
+ stua_key4(if,then,else,elseif) stua_key4(while,do,for,in)
+ stua_key4(func,var,let,break) stua_key4(nil,true,false,end)
+ stua_key4(return,continue,as,repeat) stua_key4(_frame,catch,catch,catch)
+
+ ST_shl, "<<", ST_and, "&&", ST_eq, "==", ST_ge, ">=",
+ ST_shr, ">>", ST_or , "||", ST_ne, "!=", ST_le, "<=",
+ ST_shru,">>>", ST_into, "=>",
+ T__none, ".",
+};
+
+typedef struct
+{
+ stua_obj *data; // constants being compiled
+ short *code; // code being compiled
+ stua_dict *locals;
+ short *non_local_refs;
+} stu__comp_func;
+
+static stu__comp_func stu__pfunc;
+static stu__comp_func *func_stack = NULL;
+static void stu__push_func_comp(void)
+{
+ stb_arr_push(func_stack, stu__pfunc);
+ stu__pfunc.data = NULL;
+ stu__pfunc.code = NULL;
+ stu__pfunc.locals = stb_idict_new_size(16);
+ stu__pfunc.non_local_refs = NULL;
+ stb_arr_push(stu__pfunc.code, 0); // number of data items
+ stb_arr_push(stu__pfunc.code, 1); // starting execution address
+}
+
+static void stu__pop_func_comp(void)
+{
+ stb_arr_free(stu__pfunc.code);
+ stb_arr_free(stu__pfunc.data);
+ stb_idict_destroy(stu__pfunc.locals);
+ stb_arr_free(stu__pfunc.non_local_refs);
+ stu__pfunc = stb_arr_pop(func_stack);
+}
+
+// if an id is a reference to an outer lexical scope, this
+// function returns the "name" of it, and updates the stack
+// structures to make sure the names are propagated in.
+static int stu__nonlocal_id(stua_obj var_obj)
+{
+ stua_obj dummy, var = var_obj;
+ int i, n = stb_arr_len(func_stack), j,k;
+ if (stb_idict_get_flag(stu__pfunc.locals, var, &dummy)) return 0;
+ for (i=n-1; i > 1; --i) {
+ if (stb_idict_get_flag(func_stack[i].locals, var, &dummy))
+ break;
+ }
+ if (i <= 1) return 0; // stu__compile_global_scope
+ j = i; // need to access variable from j'th frame
+ for (i=0; i < stb_arr_len(stu__pfunc.non_local_refs); ++i)
+ if (stu__pfunc.non_local_refs[i] == j) return j-n;
+ stb_arr_push(stu__pfunc.non_local_refs, j-n);
+ // now make sure all the parents propagate it down
+ for (k=n-1; k > 1; --k) {
+ if (j-k >= 0) return j-n; // comes direct from this parent
+ for(i=0; i < stb_arr_len(func_stack[k].non_local_refs); ++i)
+ if (func_stack[k].non_local_refs[i] == j-k)
+ return j-n;
+ stb_arr_push(func_stack[k].non_local_refs, j-k);
+ }
+ assert (k != 1);
+
+ return j-n;
+}
+
+static int stu__off(void) { return stb_arr_len(stu__pfunc.code); }
+static void stu__cc(int a)
+{
+ assert(a >= -2000 && a < 5000);
+ stb_arr_push(stu__pfunc.code, a);
+}
+static int stu__cc1(int a) { stu__cc(a); return stu__off()-1; }
+static int stu__cc2(int a, int b) { stu__cc(a); stu__cc(b); return stu__off()-2; }
+static int stu__cc3(int a, int b, int c) {
+ if (a == '=') assert(c != 0);
+ stu__cc(a); stu__cc(b); stu__cc(c); return stu__off()-3; }
+static int stu__cc4(int a, int b, int c, int d) { stu__cc(a); stu__cc(b); stu__cc(c); stu__cc(d); return stu__off()-4; }
+
+static int stu__cdv(stua_obj p)
+{
+ int i;
+ assert(p != STUA_NO_VALUE);
+ for (i=0; i < stb_arr_len(stu__pfunc.data); ++i)
+ if (stu__pfunc.data[i] == p)
+ break;
+ if (i == stb_arr_len(stu__pfunc.data))
+ stb_arr_push(stu__pfunc.data, p);
+ return ~i;
+}
+
+static int stu__cdt(void)
+{
+ int z = stu__cdv(stu__tokval);
+ stu__nexttoken();
+ return z;
+}
+
+static int stu__seq(int a, int b)
+{
+ return !a ? b : !b ? a : stu__cc3(STU__seq, a,b);
+}
+
+static char stu__comp_err_str[1024];
+static int stu__comp_err_line;
+static int stu__err(char *str, ...)
+{
+ va_list v;
+ char *s = stu__bufstart;
+ stu__comp_err_line = 1;
+ while (s < stu__curbuf) {
+ if (s[0] == '\n' || s[0] == '\r') {
+ if (s[0]+s[1] == '\n' + '\r') ++s;
+ ++stu__comp_err_line;
+ }
+ ++s;
+ }
+ va_start(v, str);
+ vsprintf(stu__comp_err_str, str, v);
+ va_end(v);
+ return 0;
+}
+
+static int stu__accept(int p)
+{
+ if (stu__tok != p) return 0;
+ stu__nexttoken();
+ return 1;
+}
+
+static int stu__demand(int p)
+{
+ if (stu__accept(p)) return 1;
+ return stu__err("Didn't find expected stu__tok");
+}
+
+static int stu__demandv(int p, stua_obj *val)
+{
+ if (stu__tok == p || p==0) {
+ *val = stu__tokval;
+ stu__nexttoken();
+ return 1;
+ } else
+ return 0;
+}
+
+static int stu__expr(int p);
+int stu__nexpr(int p) { stu__nexttoken(); return stu__expr(p); }
+static int stu__statements(int once, int as);
+
+static int stu__parse_if(void) // parse both ST_if and ST_elseif
+{
+ int b,c,a;
+ a = stu__nexpr(1); if (!a) return 0;
+ if (!stu__demand(ST_then)) return stu__err("expecting THEN");
+ b = stu__statements(0,0); if (!b) return 0;
+ if (b == 1) b = -1;
+
+ if (stu__tok == ST_elseif) {
+ return stu__parse_if();
+ } else if (stu__accept(ST_else)) {
+ c = stu__statements(0,0); if (!c) return 0;
+ if (!stu__demand(ST_end)) return stu__err("expecting END after else clause");
+ return stu__cc4(ST_else, a, b, c);
+ } else {
+ if (!stu__demand(ST_end)) return stu__err("expecting END in if statement");
+ return stu__cc3(ST_if, a, b);
+ }
+}
+
+int stu__varinit(int z, int in_globals)
+{
+ int a,b;
+ stu__nexttoken();
+ while (stu__demandv(ST_id, &b)) {
+ if (!stb_idict_add(stu__pfunc.locals, b, 1))
+ if (!in_globals) return stu__err("Redefined variable %s.", stu__pw(b)->ptr);
+ if (stu__accept('=')) {
+ a = stu__expr(1); if (!a) return 0;
+ } else
+ a = stu__cdv(stua_nil);
+ z = stu__seq(z, stu__cc3(ST_var, stu__cdv(b), a));
+ if (!stu__accept(',')) break;
+ }
+ return z;
+}
+
+static int stu__compile_unary(int z, int outparm, int require_inparm)
+{
+ int op = stu__tok, a, b;
+ stu__nexttoken();
+ if (outparm) {
+ if (require_inparm || (stu__tok && stu__tok != ST_end && stu__tok != ST_else && stu__tok != ST_elseif && stu__tok !=';')) {
+ a = stu__expr(1); if (!a) return 0;
+ } else
+ a = stu__cdv(stua_nil);
+ b = stu__cc2(op, a);
+ } else
+ b = stu__cc1(op);
+ return stu__seq(z,b);
+}
+
+static int stu__assign(void)
+{
+ int z;
+ stu__accept(ST_let);
+ z = stu__expr(1); if (!z) return 0;
+ if (stu__accept('=')) {
+ int y,p = (z >= 0 ? stu__pfunc.code[z] : 0);
+ if (z < 0 || (p != ST_id && p != '[')) return stu__err("Invalid lvalue in assignment");
+ y = stu__assign(); if (!y) return 0;
+ z = stu__cc3('=', z, y);
+ }
+ return z;
+}
+
+static int stu__statements(int once, int stop_while)
+{
+ int a,b, c, z=0;
+ for(;;) {
+ switch (stu__tok) {
+ case ST_if : a = stu__parse_if(); if (!a) return 0;
+ z = stu__seq(z, a);
+ break;
+ case ST_while : if (stop_while) return (z ? z:1);
+ a = stu__nexpr(1); if (!a) return 0;
+ if (stu__accept(ST_as)) c = stu__statements(0,0); else c = 0;
+ if (!stu__demand(ST_do)) return stu__err("expecting DO");
+ b = stu__statements(0,0); if (!b) return 0;
+ if (!stu__demand(ST_end)) return stu__err("expecting END");
+ if (b == 1) b = -1;
+ z = stu__seq(z, stu__cc4(ST_while, a, b, c));
+ break;
+ case ST_repeat : stu__nexttoken();
+ c = stu__statements(0,1); if (!c) return 0;
+ if (!stu__demand(ST_while)) return stu__err("expecting WHILE");
+ a = stu__expr(1); if (!a) return 0;
+ if (!stu__demand(ST_do)) return stu__err("expecting DO");
+ b = stu__statements(0,0); if (!b) return 0;
+ if (!stu__demand(ST_end)) return stu__err("expecting END");
+ if (b == 1) b = -1;
+ z = stu__seq(z, stu__cc4(ST_as, a, b, c));
+ break;
+ case ST_catch : a = stu__nexpr(1); if (!a) return 0;
+ z = stu__seq(z, stu__cc2(ST_catch, a));
+ break;
+ case ST_var : z = stu__varinit(z,0); break;
+ case ST_return : z = stu__compile_unary(z,1,1); break;
+ case ST_continue:z = stu__compile_unary(z,0,0); break;
+ case ST_break : z = stu__compile_unary(z,1,0); break;
+ case ST_into : if (z == 0 && !once) return stu__err("=> cannot be first statement in block");
+ a = stu__nexpr(99);
+ b = (a >= 0? stu__pfunc.code[a] : 0);
+ if (a < 0 || (b != ST_id && b != '[')) return stu__err("Invalid lvalue on right side of =>");
+ z = stu__cc3('=', a, z);
+ break;
+ default : if (stu__end[stu__tok]) return once ? 0 : (z ? z:1);
+ a = stu__assign(); if (!a) return 0;
+ stu__accept(';');
+ if (stu__tok && !stu__end[stu__tok]) {
+ if (a < 0)
+ return stu__err("Constant has no effect");
+ if (stu__pfunc.code[a] != '(' && stu__pfunc.code[a] != '=')
+ return stu__err("Expression has no effect");
+ }
+ z = stu__seq(z, a);
+ break;
+ }
+ if (!z) return 0;
+ stu__accept(';');
+ if (once && stu__tok != ST_into) return z;
+ }
+}
+
+static int stu__postexpr(int z, int p);
+static int stu__dictdef(int end, int *count)
+{
+ int z,n=0,i,flags=0;
+ short *dict=NULL;
+ stu__nexttoken();
+ while (stu__tok != end) {
+ if (stu__tok == ST_id) {
+ stua_obj id = stu__tokval;
+ stu__nexttoken();
+ if (stu__tok == '=') {
+ flags |= 1;
+ stb_arr_push(dict, stu__cdv(id));
+ z = stu__nexpr(1); if (!z) return 0;
+ } else {
+ z = stu__cc2(ST_id, stu__cdv(id));
+ z = stu__postexpr(z,1); if (!z) return 0;
+ flags |= 2;
+ stb_arr_push(dict, stu__cdv(stu__makeint(n++)));
+ }
+ } else {
+ z = stu__expr(1); if (!z) return 0;
+ flags |= 2;
+ stb_arr_push(dict, stu__cdv(stu__makeint(n++)));
+ }
+ if (end != ')' && flags == 3) { z=stu__err("can't mix initialized and uninitialized defs"); goto done;}
+ stb_arr_push(dict, z);
+ if (!stu__accept(',')) break;
+ }
+ if (!stu__demand(end))
+ return stu__err(end == ')' ? "Expecting ) at end of function call"
+ : "Expecting } at end of dictionary definition");
+ z = stu__cc2('{', stb_arr_len(dict)/2);
+ for (i=0; i < stb_arr_len(dict); ++i)
+ stu__cc(dict[i]);
+ if (count) *count = n;
+done:
+ stb_arr_free(dict);
+ return z;
+}
+
+static int stu__comp_id(void)
+{
+ int z,d;
+ d = stu__nonlocal_id(stu__tokval);
+ if (d == 0)
+ return z = stu__cc2(ST_id, stu__cdt());
+ // access a non-local frame by naming it with the appropriate int
+ assert(d < 0);
+ z = stu__cdv(d); // relative frame # is the 'variable' in our local frame
+ z = stu__cc2(ST_id, z); // now access that dictionary
+ return stu__cc3('[', z, stu__cdt()); // now access the variable from that dir
+}
+
+static stua_obj stu__funcdef(stua_obj *id, stua_obj *func);
+static int stu__expr(int p)
+{
+ int z;
+ // unary
+ switch (stu__tok) {
+ case ST_number: z = stu__cdt(); break;
+ case ST_string: z = stu__cdt(); break; // @TODO - string concatenation like C
+ case ST_id : z = stu__comp_id(); break;
+ case ST__frame: z = stu__cc1(ST__frame); stu__nexttoken(); break;
+ case ST_func : z = stu__funcdef(NULL,NULL); break;
+ case ST_if : z = stu__parse_if(); break;
+ case ST_nil : z = stu__cdv(stua_nil); stu__nexttoken(); break;
+ case ST_true : z = stu__cdv(stua_true); stu__nexttoken(); break;
+ case ST_false : z = stu__cdv(stua_false); stu__nexttoken(); break;
+ case '-' : z = stu__nexpr(99); if (z) z=stu__cc2(STU__negate,z); else return z; break;
+ case '!' : z = stu__nexpr(99); if (z) z=stu__cc2('!',z); else return z; break;
+ case '~' : z = stu__nexpr(99); if (z) z=stu__cc2('~',z); else return z; break;
+ case '{' : z = stu__dictdef('}', NULL); break;
+ default : return stu__err("Unexpected token");
+ case '(' : stu__nexttoken(); z = stu__statements(0,0); if (!stu__demand(')')) return stu__err("Expecting )");
+ }
+ return stu__postexpr(z,p);
+}
+
+static int stu__postexpr(int z, int p)
+{
+ int q;
+ // postfix
+ while (stu__tok == '(' || stu__tok == '[' || stu__tok == '.') {
+ if (stu__accept('.')) {
+ // MUST be followed by a plain identifier! use [] for other stuff
+ if (stu__tok != ST_id) return stu__err("Must follow . with plain name; try [] instead");
+ z = stu__cc3('[', z, stu__cdv(stu__tokval));
+ stu__nexttoken();
+ } else if (stu__accept('[')) {
+ while (stu__tok != ']') {
+ int r = stu__expr(1); if (!r) return 0;
+ z = stu__cc3('[', z, r);
+ if (!stu__accept(',')) break;
+ }
+ if (!stu__demand(']')) return stu__err("Expecting ]");
+ } else {
+ int n, p = stu__dictdef(')', &n); if (!p) return 0;
+ #if 0 // this is incorrect!
+ if (z > 0 && stu__pfunc.code[z] == ST_id) {
+ stua_obj q = stu__get(stu__globaldict, stu__pfunc.data[-stu__pfunc.code[z+1]-1], stua_nil);
+ if (stu__checkt(STU___function, q))
+ if ((stu__pf(q))->num_param != n)
+ return stu__err("Incorrect number of parameters");
+ }
+ #endif
+ z = stu__cc3('(', z, p);
+ }
+ }
+ // binop - this implementation taken from lcc
+ for (q=stu__prec[stu__tok]; q >= p; --q) {
+ while (stu__prec[stu__tok] == q) {
+ int o = stu__tok, y = stu__nexpr(p+1); if (!y) return 0;
+ z = stu__cc3(o,z,y);
+ }
+ }
+ return z;
+}
+
+static stua_obj stu__finish_func(stua_obj *param, int start)
+{
+ int n, size;
+ stu__func *f = (stu__func *) malloc(sizeof(*f));
+ f->closure_source = 0;
+ f->num_param = stb_arr_len(param);
+ f->param = (int *) stb_copy(param, f->num_param * sizeof(*f->param));
+ size = stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data) + sizeof(*f) + 8;
+ f->f.store = malloc(stb_arr_storage(stu__pfunc.code) + stb_arr_storage(stu__pfunc.data));
+ f->code = (short *) ((char *) f->f.store + stb_arr_storage(stu__pfunc.data));
+ memcpy(f->code, stu__pfunc.code, stb_arr_storage(stu__pfunc.code));
+ f->code[1] = start;
+ f->code[0] = stb_arr_len(stu__pfunc.data);
+ for (n=0; n < f->code[0]; ++n)
+ ((stua_obj *) f->code)[-1-n] = stu__pfunc.data[n];
+ return stu__makeobj(STU___function, f, size, 0);
+}
+
+static int stu__funcdef(stua_obj *id, stua_obj *result)
+{
+ int n,z=0,i,q;
+ stua_obj *param = NULL;
+ short *nonlocal;
+ stua_obj v,f=stua_nil;
+ assert(stu__tok == ST_func);
+ stu__nexttoken();
+ if (id) {
+ if (!stu__demandv(ST_id, id)) return stu__err("Expecting function name");
+ } else
+ stu__accept(ST_id);
+ if (!stu__demand('(')) return stu__err("Expecting ( for function parameter");
+ stu__push_func_comp();
+ while (stu__tok != ')') {
+ if (!stu__demandv(ST_id, &v)) { z=stu__err("Expecting parameter name"); goto done; }
+ stb_idict_add(stu__pfunc.locals, v, 1);
+ if (stu__tok == '=') {
+ n = stu__nexpr(1); if (!n) { z=0; goto done; }
+ z = stu__seq(z, stu__cc3(STU__defaultparm, stu__cdv(v), n));
+ } else
+ stb_arr_push(param, v);
+ if (!stu__accept(',')) break;
+ }
+ if (!stu__demand(')')) { z=stu__err("Expecting ) at end of parameter list"); goto done; }
+ n = stu__statements(0,0); if (!n) { z=0; goto done; }
+ if (!stu__demand(ST_end)) { z=stu__err("Expecting END at end of function"); goto done; }
+ if (n == 1) n = 0;
+ n = stu__seq(z,n);
+ f = stu__finish_func(param, n);
+ if (result) { *result = f; z=1; stu__pop_func_comp(); }
+ else {
+ nonlocal = stu__pfunc.non_local_refs;
+ stu__pfunc.non_local_refs = NULL;
+ stu__pop_func_comp();
+ z = stu__cdv(f);
+ if (nonlocal) { // build a closure with references to the needed frames
+ short *initcode = NULL;
+ for (i=0; i < stb_arr_len(nonlocal); ++i) {
+ int k = nonlocal[i], p;
+ stb_arr_push(initcode, stu__cdv(k));
+ if (k == -1) p = stu__cc1(ST__frame);
+ else { p = stu__cdv(stu__makeint(k+1)); p = stu__cc2(ST_id, p); }
+ stb_arr_push(initcode, p);
+ }
+ q = stu__cc2('{', stb_arr_len(nonlocal));
+ for (i=0; i < stb_arr_len(initcode); ++i)
+ stu__cc(initcode[i]);
+ z = stu__cc3('+', z, q);
+ stb_arr_free(initcode);
+ }
+ stb_arr_free(nonlocal);
+ }
+done:
+ stb_arr_free(param);
+ if (!z) stu__pop_func_comp();
+ return z;
+}
+
+static int stu__compile_global_scope(void)
+{
+ stua_obj o;
+ int z=0;
+
+ stu__push_func_comp();
+ while (stu__tok != 0) {
+ if (stu__tok == ST_func) {
+ stua_obj id, f;
+ if (!stu__funcdef(&id,&f))
+ goto error;
+ stu__set(stu__globaldict, id, f);
+ } else if (stu__tok == ST_var) {
+ z = stu__varinit(z,1); if (!z) goto error;
+ } else {
+ int y = stu__statements(1,0); if (!y) goto error;
+ z = stu__seq(z,y);
+ }
+ stu__accept(';');
+ }
+ o = stu__finish_func(NULL, z);
+ stu__pop_func_comp();
+
+ o = stu__funceval(o, stua_globals); // initialize stu__globaldict
+ if (stu__flow == FLOW_error)
+ printf("Error: %s\n", ((stu__wrapper *) stu__ptr(stu__flow_val))->ptr);
+ return 1;
+error:
+ stu__pop_func_comp();
+ return 0;
+}
+
+stua_obj stu__myprint(stua_dict *context)
+{
+ stua_obj x = stu__get(context, stua_string("x"), stua_nil);
+ if ((x & 1) == stu__float_tag) printf("%f", stu__getfloat(x));
+ else if (stu__tag(x) == stu__int_tag) printf("%d", stu__int(x));
+ else {
+ stu__wrapper *s = stu__pw(x);
+ if (s->type == STU___string || s->type == STU___error)
+ printf("%s", s->ptr);
+ else if (s->type == STU___dict) printf("{{dictionary}}");
+ else if (s->type == STU___function) printf("[[function]]");
+ else
+ printf("[[ERROR:%s]]", s->ptr);
+ }
+ return x;
+}
+
+void stua_init(void)
+{
+ if (!stu__globaldict) {
+ int i;
+ stua_obj s;
+ stu__func *f;
+
+ stu__prec[ST_and] = stu__prec[ST_or] = 1;
+ stu__prec[ST_eq ] = stu__prec[ST_ne] = stu__prec[ST_le] =
+ stu__prec[ST_ge] = stu__prec['>' ] = stu__prec['<'] = 2;
+ stu__prec[':'] = 3;
+ stu__prec['&'] = stu__prec['|'] = stu__prec['^'] = 4;
+ stu__prec['+'] = stu__prec['-'] = 5;
+ stu__prec['*'] = stu__prec['/'] = stu__prec['%'] =
+ stu__prec[ST_shl]= stu__prec[ST_shr]= stu__prec[ST_shru]= 6;
+
+ stu__end[')'] = stu__end[ST_end] = stu__end[ST_else] = 1;
+ stu__end[ST_do] = stu__end[ST_elseif] = 1;
+
+ stu__float_init();
+ stu__lex_matcher = stb_lex_matcher();
+ for (i=0; i < sizeof(stu__lexemes)/sizeof(stu__lexemes[0]); ++i)
+ stb_lex_item(stu__lex_matcher, stu__lexemes[i].regex, stu__lexemes[i].stu__tok);
+
+ stu__globaldict = stb_idict_new_size(64);
+ stua_globals = stu__makeobj(STU___dict, stu__globaldict, 0,0);
+ stu__strings = stb_sdict_new(0);
+
+ stu__curbuf = stu__bufstart = "func _print(x) end\n"
+ "func print()\n var x=0 while _frame[x] != nil as x=x+1 do _print(_frame[x]) end end\n";
+ stu__nexttoken();
+ if (!stu__compile_global_scope())
+ printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str);
+
+ s = stu__get(stu__globaldict, stua_string("_print"), stua_nil);
+ if (stu__tag(s) == stu__ptr_tag && stu__ptr(s)->type == STU___function) {
+ f = stu__pf(s);
+ free(f->f.store);
+ f->closure_source = 4;
+ f->f.func = stu__myprint;
+ f->code = NULL;
+ }
+ }
+}
+
+void stua_uninit(void)
+{
+ if (stu__globaldict) {
+ stb_idict_remove_all(stu__globaldict);
+ stb_arr_setlen(stu__gc_root_stack, 0);
+ stua_gc(1);
+ stb_idict_destroy(stu__globaldict);
+ stb_sdict_delete(stu__strings);
+ stb_matcher_free(stu__lex_matcher);
+ stb_arr_free(stu__gc_ptrlist);
+ stb_arr_free(func_stack);
+ stb_arr_free(stu__gc_root_stack);
+ stu__globaldict = NULL;
+ }
+}
+
+void stua_run_script(char *s)
+{
+ stua_init();
+
+ stu__curbuf = stu__bufstart = s;
+ stu__nexttoken();
+
+ stu__flow = FLOW_normal;
+
+ if (!stu__compile_global_scope())
+ printf("Compile error in line %d: %s\n", stu__comp_err_line, stu__comp_err_str);
+ stua_gc(1);
+}
+#endif // STB_DEFINE
+#endif // STB_STUA
+
+#undef STB_EXTERN
+#endif // STB_INCLUDE_STB_H
+
+/*
+------------------------------------------------------------------------------
+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/src/libs/decoders/stb_vorbis.h b/src/libs/decoders/stb_vorbis.h
new file mode 100644
index 000000000..f263e5486
--- /dev/null
+++ b/src/libs/decoders/stb_vorbis.h
@@ -0,0 +1,5624 @@
+// Ogg Vorbis audio decoder - v1.17 - 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 Maxwell Koo
+//
+// Partial history:
+// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)
+// 1.16 - 2019-03-04 - fix warnings
+// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
+// 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 <stdio.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#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. Moreover, 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,
+ VORBIS_ogg_skeleton_not_supported
+};
+
+
+#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 <stdio.h>
+#endif
+
+#ifndef STB_VORBIS_NO_CRT
+ #include <stdlib.h>
+ #include <string.h>
+ #include <assert.h>
+ #include <math.h>
+
+ // find definition of alloca if it's not in stdlib.h:
+ #if defined(_MSC_VER) || defined(__MINGW32__)
+ #include <malloc.h>
+ #endif
+ #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__)
+ #include <alloca.h>
+ #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 <limits.h>
+
+#ifdef __MINGW32__
+ // eff you mingw:
+ // "fixed":
+ // http://sourceforge.net/p/mingw-w64/mailman/message/32882927/
+ // "no that broke the build, reverted, who cares about C":
+ // http://sourceforge.net/p/mingw-w64/mailman/message/32890381/
+ #ifdef __forceinline
+ #undef __forceinline
+ #endif
+ #define __forceinline
+ #define alloca __builtin_alloca
+#elif !defined(_MSC_VER)
+ #if __GNUC__
+ #define __forceinline inline
+ #else
+ #define __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 <crtdbg.h>
+#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;
+ }
+}
+
+static __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);
+ // propagate 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];
+ }
+ }
+ }
+}
+
+#ifdef _MSC_VER
+#define STBV_CDECL __cdecl
+#else
+#define STBV_CDECL
+#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
+ if (pow((float) r+1, dim) <= entries)
+ return -1;
+ if ((int) floor(pow((float) r, dim)) > entries)
+ return -1;
+ 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
+static __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
+
+static __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&255]);
+ 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&255]);
+ }
+ }
+}
+
+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. __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;
+ }
+}
+
+static __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 propagates 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];
+ 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);
+ if (w == NULL) return 0;
+ 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) {
+ // check for the Ogg skeleton fishead identifying header to refine our error
+ if (f->segments[0] == 64 &&
+ getn(f, header, 6) &&
+ header[0] == 'f' &&
+ header[1] == 'i' &&
+ header[2] == 's' &&
+ header[3] == 'h' &&
+ header[4] == 'e' &&
+ header[5] == 'a' &&
+ get8(f) == 'd' &&
+ get8(f) == '\0') return error(f, VORBIS_ogg_skeleton_not_supported);
+ else
+ 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_length >= 32) return error(f, VORBIS_invalid_setup);
+ 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) {
+ int values = lookup1_values(c->entries, c->dimensions);
+ if (values < 0) return error(f, VORBIS_invalid_setup);
+ c->lookup_values = (uint32) values;
+ } 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]; // under review at https://github.com/nothings/stb/issues/816
+ float 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-1; ++j)
+ if (p[j].x == p[j+1].x)
+ return error(f, VORBIS_invalid_setup);
+ 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 = 0;
+ int hi = 0;
+ 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;
+ if (m->coupling_steps > f->channels) return error(f, VORBIS_invalid_setup);
+ 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 preceding 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 = 0;
+ double bytes_per_sample = 0;
+ 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; -- value is never read
+ 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;
+#if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
+ if (0 != fopen_s(&f, filename, "rb"))
+ f = NULL;
+#else
+ f = fopen(filename, "rb");
+#endif
+ 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_RWseek(rwops, 0, RW_SEEK_END) - start);
+ SDL_RWseek(rwops, start, RW_SEEK_SET);
+ 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) {
+ *f = p;
+ 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 = NULL;
+ 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; dead code
+ 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; dead code
+ // 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.17 - 2019-07-08 - fix CVE-2019-13217, -13218, -13219, -13220, -13221, -13222, -13223
+ found with Mayhem by ForAllSecure
+ 1.16 - 2019-03-04 - fix warnings
+ 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found
+ 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 '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 <malloc.h> 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/src/libs/decoders/stb_vorbis_test.c b/src/libs/decoders/stb_vorbis_test.c
new file mode 100644
index 000000000..684cb472e
--- /dev/null
+++ b/src/libs/decoders/stb_vorbis_test.c
@@ -0,0 +1,262 @@
+#define STB_DEFINE
+#define STB_ONLY
+#include "stb.h" /* http://nothings.org/stb.h */
+#include "stb_vorbis.h"
+
+void write_floats(FILE *g, int len, float *left, float *right);
+void show_info(stb_vorbis *v);
+
+// stb_vorbis_decode_filename: decode an entire file to interleaved shorts
+void test_decode_filename(FILE *g, char *filename)
+{
+ short *decoded;
+ int channels, len, sample_rate;
+ len = stb_vorbis_decode_filename(filename, &channels, &sample_rate, &decoded);
+
+ if (len)
+ fwrite(decoded, 2, len*channels, g);
+ else
+ stb_fatal("Couldn't open {%s}", filename);
+}
+
+void test_get_frame_short_interleaved(FILE *g, char *filename)
+{
+ short sbuffer[8000];
+ int n, error;
+ stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
+ if (!v) stb_fatal("Couldn't open {%s} due to error: %d", filename, error);
+ show_info(v);
+
+ while (0 != (n=stb_vorbis_get_frame_short_interleaved(v, 2, sbuffer, 8000))) {
+ fwrite(sbuffer, 2, n*2, g);
+ }
+ stb_vorbis_close(v);
+}
+
+void test_get_samples_short_interleaved(FILE *g, char *filename)
+{
+ int error;
+ stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
+ if (!v) stb_fatal("Couldn't open {%s}", filename);
+ show_info(v);
+
+ for(;;) {
+ int16 sbuffer[333];
+ int n;
+ n = stb_vorbis_get_samples_short_interleaved(v, 2, sbuffer, 333);
+ if (n == 0)
+ break;
+ fwrite(sbuffer, 2, n*2, g);
+ }
+ stb_vorbis_close(v);
+}
+
+void test_get_frame_float(FILE *g, char *filename)
+{
+ int error;
+ stb_vorbis *v = stb_vorbis_open_filename(filename, &error, NULL);
+ if (!v) stb_fatal("Couldn't open {%s}", filename);
+ show_info(v);
+
+ for(;;) {
+ int n;
+ float *left, *right;
+ float **outputs;
+ int num_c;
+ n = stb_vorbis_get_frame_float(v, &num_c, &outputs);
+ if (n == 0)
+ break;
+ left = outputs[0];
+ right = outputs[num_c > 1];
+ write_floats(g, n, left, right);
+ }
+ stb_vorbis_close(v);
+}
+
+
+// in push mode, you can load your data any way you want, then
+// feed it a little bit at a time. this is the preferred way to
+// handle reading from a packed file or some custom stream format;
+// instead of putting callbacks inside stb_vorbis, you just keep
+// a little buffer (it needs to be big enough for one packet of
+// audio, except at the beginning where you need to buffer up the
+// entire header).
+//
+// for this test, I just load all the data and just lie to stb_vorbis
+// and claim I only have a little of it
+void test_decode_frame_pushdata(FILE *g, char *filename)
+{
+ int p,q, len, error, used;
+ stb_vorbis *v;
+ uint8 *data = stb_file(filename, &len);
+
+ if (!data) stb_fatal("Couldn't open {%s}", filename);
+
+ p = 0;
+ q = 1;
+ retry:
+ v = stb_vorbis_open_pushdata(data, q, &used, &error, NULL);
+ if (v == NULL) {
+ if (error == VORBIS_need_more_data) {
+ q += 1;
+ goto retry;
+ }
+ fprintf(stderr, "Error %d\n", error);
+ exit(1);
+ }
+ p += used;
+
+ show_info(v);
+
+ for(;;) {
+ int k=0;
+ int n;
+ float *left, *right;
+ uint32 next_t=0;
+
+ float **outputs;
+ int num_c;
+ q = 32;
+ retry3:
+ if (q > len-p) q = len-p;
+ used = stb_vorbis_decode_frame_pushdata(v, data+p, q, &num_c, &outputs, &n);
+ if (used == 0) {
+ if (p+q == len) break; // no more data, stop
+ if (q < 128) q = 128;
+ q *= 2;
+ goto retry3;
+ }
+ p += used;
+ if (n == 0) continue; // seek/error recovery
+ left = outputs[0];
+ right = num_c > 1 ? outputs[1] : outputs[0];
+ write_floats(g, n, left, right);
+ }
+ stb_vorbis_close(v);
+}
+
+void write_floats(FILE *g, int len, float *left, float *right)
+{
+ const float scale = 32768;
+ int j;
+ for (j=0; j < len; ++j) {
+ int16 x,y;
+ x = (int) stb_clamp((int) (scale * left[j]), -32768, 32767);
+ y = (int) stb_clamp((int) (scale * right[j]), -32768, 32767);
+ fwrite(&x, 2, 1, g);
+ fwrite(&y, 2, 1, g);
+ }
+}
+
+void show_info(stb_vorbis *v)
+{
+ if (v) {
+ stb_vorbis_info info = stb_vorbis_get_info(v);
+ printf("%d channels, %d samples/sec\n", info.channels, info.sample_rate);
+ printf("Predicted memory needed: %d (%d + %d)\n", info.setup_memory_required + info.temp_memory_required,
+ info.setup_memory_required, info.temp_memory_required);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ FILE *g=NULL;
+ char *outfile = "vorbis_test.out";
+ char *infile;
+ int n=0;
+
+ if (argc > 1 && argv[1][1] == 0 && argv[1][0] >= '1' && argv[1][0] <= '5')
+ n = atoi(argv[1]);
+
+ if (argc < 3 || argc > 4 || n == 0) {
+ stbprint("Usage: sample {code} {vorbis-filename} [{output-filename}]\n"
+ "Code is one of:\n"
+ " 1 - test stb_vorbis_decode_filename\n"
+ " 2 - test stb_vorbis_get_frame_short_interleaved\n"
+ " 3 - test stb_vorbis_get_samples_short_interleaved\n"
+ " 4 - test stb_vorbis_get_frame_float\n"
+ " 5 - test stb_vorbis_decode_frame_pushdata\n");
+ return argc != 1;
+ }
+
+ infile = argv[2];
+
+ if (argc > 3)
+ outfile = argv[3];
+ if (strlen(outfile) >= 4 && 0==stb_stricmp(outfile + strlen(outfile) - 4, ".ogg"))
+ stb_fatal("You specified a .ogg file as your output file, which you probably didn't actually want.");
+
+ if (!strcmp(outfile, "stdout") || !strcmp(outfile, "-") || !strcmp(outfile, "-stdout"))
+ g = stdout;
+ else
+ g = fopen(outfile, "wb");
+ if (!g) stb_fatal("Couldn't open {%s} for writing", outfile);
+
+ switch (n) {
+ case 1: test_decode_filename(g, infile); break;
+ case 2: test_get_frame_short_interleaved(g, infile); break;
+ case 3: test_get_samples_short_interleaved(g, infile); break;
+ case 4: test_get_frame_float(g, infile); break;
+ case 5: test_decode_frame_pushdata(g, infile); break;
+ default: stb_fatal("Unknown option {%d}", n);
+ }
+ fclose(g);
+ return 0;
+}
+
+void test_push_mode_forever(FILE *g, char *filename)
+{
+ int p,q, len, error, used;
+ uint8 *data = stb_file(filename, &len);
+ stb_vorbis *v;
+
+ if (!data) stb_fatal("Couldn't open {%s}", filename);
+
+ p = 0;
+ q = 1;
+ retry:
+ v = stb_vorbis_open_pushdata(data, q, &used, &error, NULL);
+ if (v == NULL) {
+ if (error == VORBIS_need_more_data) {
+ q += 1;
+ goto retry;
+ }
+ printf("Error %d\n", error);
+ exit(1);
+ }
+ p += used;
+
+ show_info(v);
+
+ for(;;) {
+ int k=0;
+ int n;
+ float *left, *right;
+ float **outputs;
+ int num_c;
+ q = 32;
+ retry3:
+ if (q > len-p) q = len-p;
+ used = stb_vorbis_decode_frame_pushdata(v, data+p, q, &num_c, &outputs, &n);
+ if (used == 0) {
+ if (p+q == len) {
+ // seek randomly when at end... this makes sense when listening to it, but dumb when writing to file
+ p = stb_rand();
+ if (p < 0) p = -p;
+ p %= (len - 8000);
+ stb_vorbis_flush_pushdata(v);
+ q = 128;
+ goto retry3;
+ }
+ if (q < 128) q = 128;
+ q *= 2;
+ goto retry3;
+ }
+ p += used;
+ if (n == 0) continue;
+ left = outputs[0];
+ right = num_c > 1 ? outputs[1] : outputs[0];
+ write_floats(g, n, left, right);
+ }
+ stb_vorbis_close(v);
+}
diff --git a/src/libs/decoders/vorbis.c b/src/libs/decoders/vorbis.c
new file mode 100644
index 000000000..6c37989c7
--- /dev/null
+++ b/src/libs/decoders/vorbis.c
@@ -0,0 +1,238 @@
+/*
+ * This DOSBox Vorbis decoder backend maintained by Kevin R. Croft (krcroft@gmail.com)
+ * This decoder makes use of the excellent STB Vorbis decoder by Sean Barrett
+ *
+ * Source links
+ * - STB: https://github.com/nothings/stb (source)
+ * - STB: https://twitter.com/nothings (website/author info)
+ *
+ * The upstream SDL2 Sound 1.9.x Vorbis decoder is written and copyright by Ryan C. Gordon. (icculus@icculus.org)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file is part of the SDL Sound Library.
+ *
+ * This Vorbis decoder backend is free software: you can redistribute
+ * it and/or modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This SDL_sound Ogg Opus decoder backend is distributed in the hope that it
+ * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the SDL Sound Library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef memcpy
+# undef memcpy
+#endif
+
+#include <string.h> // memcpy
+#include <math.h>
+
+#include "SDL_sound.h"
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h"
+
+#ifdef asset
+# undef assert
+# define assert SDL_assert
+#endif
+
+#ifdef memset
+# undef memset
+# define memset SDL_memset
+#endif
+
+#define memcmp SDL_memcmp
+#define qsort SDL_qsort
+#define malloc SDL_malloc
+#define realloc SDL_realloc
+#define free SDL_free
+#define dealloca(x) SDL_stack_free((x))
+
+/* 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 2
+#define STBV_CDECL
+// #define STB_FORCEINLINE SDL_FORCE_INLINE
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+#define STB_VORBIS_BIG_ENDIAN 1
+#endif
+
+#include "stb_vorbis.h"
+
+#ifdef DEBUG_CHATTER
+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_file_open_failure: return "VORBIS: failed opening the file";
+ 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";
+ case VORBIS_ogg_skeleton_not_supported: return "VORBIS: multi-track streams are not supported; "
+ "consider re-encoding without the Ogg Skeleton bitstream";
+ default: break;
+ } /* switch */
+
+ return "VORBIS: unknown error";
+} /* vorbis_error_string */
+#endif
+
+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);
+
+ if(stb == NULL) {
+ SNDDBG(("%s (error code: %d)\n", vorbis_error_string(err), err));
+ return 0;
+ }
+ internal->decoder_private = stb;
+ sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
+ sample->actual.format = AUDIO_S16SYS; // returns byte-order native to the running architecture
+ sample->actual.channels = stb->channels;
+ sample->actual.rate = stb->sample_rate;
+ const unsigned int 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 (int16_t));
+
+ stb_vorbis_get_error(stb); /* clear any error state */
+ rc = stb_vorbis_get_samples_short_interleaved(stb, channels, (int16_t *) internal->buffer, want_samples);
+ retval = (Uint32) (rc * channels * sizeof (int16_t)); /* 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;
+ } /* 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;
+
+ if (!stb_vorbis_seek_start(stb)) {
+ SNDDBG(("%s\n", vorbis_error_string(stb_vorbis_get_error(stb))));
+ return 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;
+
+ if(!stb_vorbis_seek(stb, sampnum)) {
+ SNDDBG(("%s\n", vorbis_error_string(stb_vorbis_get_error(stb))));
+ return 0;
+ }
+ return 1;
+} /* VORBIS_seek */
+
+
+static const char *extensions_vorbis[] = { "OGG", "OGA", "VORBIS", NULL };
+const Sound_DecoderFunctions __Sound_DecoderFunctions_VORBIS =
+{
+ {
+ extensions_vorbis,
+ "Ogg Vorbis audio",
+ "Ryan C. Gordon <icculus@icculus.org>",
+ "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 */
+};
+
+/* end of SDL_sound_vorbis.c ... */
diff --git a/src/libs/decoders/wav.c b/src/libs/decoders/wav.c
new file mode 100644
index 000000000..adc4ac4bb
--- /dev/null
+++ b/src/libs/decoders/wav.c
@@ -0,0 +1,171 @@
+/*
+ * DOSBox WAV decoder is maintained by Kevin R. Croft (krcroft@gmail.com)
+ * This decoder makes use of the excellent dr_wav library by David Reid (mackron@gmail.com)
+ *
+ * Source links
+ * - dr_libs: https://github.com/mackron/dr_libs (source)
+ * - dr_wav: http://mackron.github.io/dr_wav.html (website)
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the SDL Sound Library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "SDL_sound.h"
+#define __SDL_SOUND_INTERNAL__
+#include "SDL_sound_internal.h"
+
+/* Map dr_wav's memory routines to SDL's */
+#define DRWAV_FREE(p) SDL_free((p))
+#define DRWAV_MALLOC(sz) SDL_malloc((sz))
+#define DRWAV_REALLOC(p, sz) SDL_realloc((p), (sz))
+#define DRWAV_ZERO_MEMORY(p, sz) SDL_memset((p), 0, (sz))
+#define DRWAV_COPY_MEMORY(dst, src, sz) SDL_memcpy((dst), (src), (sz))
+
+#define DR_WAV_NO_STDIO
+#define DR_WAV_IMPLEMENTATION
+#include "dr_wav.h"
+
+static size_t wav_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;
+
+ while (retval < bytesToRead)
+ {
+ const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead);
+ if (rc == 0)
+ {
+ sample->flags |= SOUND_SAMPLEFLAG_EOF;
+ break;
+ } /* if */
+ else
+ {
+ retval += rc;
+ ptr += rc;
+ } /* else */
+ } /* while */
+
+ return retval;
+} /* wav_read */
+
+static drwav_bool32 wav_seek(void* pUserData, int offset, drwav_seek_origin origin)
+{
+ const int whence = (origin == drwav_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) ? DRWAV_TRUE : DRWAV_FALSE;
+} /* wav_seek */
+
+
+static int WAV_init(void)
+{
+ return 1; /* always succeeds. */
+} /* WAV_init */
+
+
+static void WAV_quit(void)
+{
+ /* it's a no-op. */
+} /* WAV_quit */
+
+static void WAV_close(Sound_Sample *sample)
+{
+ Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+ drwav *dr = (drwav *) internal->decoder_private;
+ if (dr != NULL) {
+ (void) drwav_uninit(dr);
+ SDL_free(dr);
+ internal->decoder_private = NULL;
+ }
+ return;
+} /* WAV_close */
+
+static int WAV_open(Sound_Sample *sample, const char *ext)
+{
+ Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+ drwav* dr = SDL_malloc(sizeof(drwav));
+ drwav_result result = drwav_init_ex(dr, wav_read, wav_seek, NULL, sample, NULL, 0, NULL);
+ internal->decoder_private = dr;
+
+ if (result == DRWAV_TRUE) {
+ SNDDBG(("WAV: Codec accepted the data stream.\n"));
+ sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
+ sample->actual.rate = dr->sampleRate;
+ sample->actual.format = AUDIO_S16SYS;
+ sample->actual.channels = dr->channels;
+
+ const Uint64 frames = (Uint64) dr->totalPCMFrameCount;
+ if (frames == 0)
+ internal->total_time = -1;
+ else {
+ const Uint32 rate = (Uint32) dr->sampleRate;
+ internal->total_time = (frames / rate) * 1000;
+ internal->total_time += ((frames % rate) * 1000) / rate;
+ } /* else */
+
+ } /* if result != DRWAV_TRUE */
+ else {
+ SNDDBG(("WAV: Codec could not parse the data stream.\n"));
+ WAV_close(sample);
+ }
+ return result;
+} /* WAV_open */
+
+
+static Uint32 WAV_read(Sound_Sample *sample)
+{
+ Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+ drwav *dr = (drwav *) internal->decoder_private;
+ const drwav_uint64 frames_read = drwav_read_pcm_frames_s16(dr,
+ internal->buffer_size / (dr->channels * sizeof(drwav_int16)),
+ (drwav_int16 *) internal->buffer);
+ return frames_read * dr->channels * sizeof (drwav_int16);
+} /* WAV_read */
+
+
+static int WAV_rewind(Sound_Sample *sample)
+{
+ Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+ drwav *dr = (drwav *) internal->decoder_private;
+ return (drwav_seek_to_pcm_frame(dr, 0) == DRWAV_TRUE);
+} /* WAV_rewind */
+
+static int WAV_seek(Sound_Sample *sample, Uint32 ms)
+{
+ Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
+ drwav *dr = (drwav *) internal->decoder_private;
+ const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
+ const drwav_uint64 frame_offset = (drwav_uint64) (frames_per_ms * ((float) ms));
+ return (drwav_seek_to_pcm_frame(dr, frame_offset) == DRWAV_TRUE);
+} /* WAV_seek */
+
+static const char *extensions_wav[] = { "WAV", "W64", NULL };
+
+const Sound_DecoderFunctions __Sound_DecoderFunctions_WAV =
+{
+ {
+ extensions_wav,
+ "WAV Audio Codec",
+ "Kevin R. Croft <krcroft@gmail.com>",
+ "github.com/mackron/dr_libs/blob/master/dr_wav.h"
+ },
+
+ 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 */
+};
+/* end of wav.c ... */
diff --git a/src/libs/decoders/xxh3.h b/src/libs/decoders/xxh3.h
new file mode 100644
index 000000000..421bf2541
--- /dev/null
+++ b/src/libs/decoders/xxh3.h
@@ -0,0 +1,1652 @@
+/*
+ xxHash - Extremely Fast Hash algorithm
+ Development source file for `xxh3`
+ Copyright (C) 2019-present, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+/* Note :
+ This file is separated for development purposes.
+ It will be integrated into `xxhash.c` when development phase is complete.
+*/
+
+#ifndef XXH3_H
+#define XXH3_H
+
+
+/* === Dependencies === */
+
+#undef XXH_INLINE_ALL /* in case it's already defined */
+#define XXH_INLINE_ALL
+#include "xxhash.h"
+
+
+/* === Compiler specifics === */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* >= C99 */
+# define XXH_RESTRICT restrict
+#else
+/* note : it might be useful to define __restrict or __restrict__ for some C++ compilers */
+# define XXH_RESTRICT /* disable */
+#endif
+
+#if defined(__GNUC__)
+# if defined(__AVX2__)
+# include <immintrin.h>
+# elif defined(__SSE2__)
+# include <emmintrin.h>
+# elif defined(__ARM_NEON__) || defined(__ARM_NEON)
+# define inline __inline__ /* clang bug */
+# include <arm_neon.h>
+# undef inline
+# endif
+#elif defined(_MSC_VER)
+# include <intrin.h>
+#endif
+
+
+
+/* ==========================================
+ * Vectorization detection
+ * ========================================== */
+#define XXH_SCALAR 0
+#define XXH_SSE2 1
+#define XXH_AVX2 2
+#define XXH_NEON 3
+#define XXH_VSX 4
+
+#ifndef XXH_VECTOR /* can be defined on command line */
+# if defined(__AVX2__)
+# define XXH_VECTOR XXH_AVX2
+# elif defined(__SSE2__) || defined(_M_AMD64) || defined(_M_X64) || (defined(_M_IX86_FP) && (_M_IX86_FP == 2))
+# define XXH_VECTOR XXH_SSE2
+# elif defined(__GNUC__) /* msvc support maybe later */ \
+ && (defined(__ARM_NEON__) || defined(__ARM_NEON)) \
+ && defined(__LITTLE_ENDIAN__) /* ARM big endian is a thing */
+# define XXH_VECTOR XXH_NEON
+# elif defined(__PPC64__) && defined(__POWER8_VECTOR__) && defined(__GNUC__)
+# define XXH_VECTOR XXH_VSX
+# else
+# define XXH_VECTOR XXH_SCALAR
+# endif
+#endif
+
+/* control alignment of accumulator,
+ * for compatibility with fast vector loads */
+#ifndef XXH_ACC_ALIGN
+# if XXH_VECTOR == 0 /* scalar */
+# define XXH_ACC_ALIGN 8
+# elif XXH_VECTOR == 1 /* sse2 */
+# define XXH_ACC_ALIGN 16
+# elif XXH_VECTOR == 2 /* avx2 */
+# define XXH_ACC_ALIGN 32
+# elif XXH_VECTOR == 3 /* neon */
+# define XXH_ACC_ALIGN 16
+# elif XXH_VECTOR == 4 /* vsx */
+# define XXH_ACC_ALIGN 16
+# endif
+#endif
+
+/* U64 XXH_mult32to64(U32 a, U64 b) { return (U64)a * (U64)b; } */
+#if defined(_MSC_VER) && defined(_M_IX86)
+# include <intrin.h>
+# define XXH_mult32to64(x, y) __emulu(x, y)
+#else
+# define XXH_mult32to64(x, y) ((U64)((x) & 0xFFFFFFFF) * (U64)((y) & 0xFFFFFFFF))
+#endif
+
+/* VSX stuff. It's a lot because VSX support is mediocre across compilers and
+ * there is a lot of mischief with endianness. */
+#if XXH_VECTOR == XXH_VSX
+# include <altivec.h>
+# undef vector
+typedef __vector unsigned long long U64x2;
+typedef __vector unsigned char U8x16;
+typedef __vector unsigned U32x4;
+
+#ifndef XXH_VSX_BE
+# ifdef __BIG_ENDIAN__
+# define XXH_VSX_BE 1
+# elif defined(__VEC_ELEMENT_REG_ORDER__) && __VEC_ELEMENT_REG_ORDER__ == __ORDER_BIG_ENDIAN__
+# warning "-maltivec=be is not recommended. Please use native endianness."
+# define XXH_VSX_BE 1
+# else
+# define XXH_VSX_BE 0
+# endif
+#endif
+
+/* We need some helpers for big endian mode. */
+#if XXH_VSX_BE
+/* A wrapper for POWER9's vec_revb. */
+# ifdef __POWER9_VECTOR__
+# define XXH_vec_revb vec_revb
+# else
+XXH_FORCE_INLINE U64x2 XXH_vec_revb(U64x2 val)
+{
+ U8x16 const vByteSwap = { 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+ 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08 };
+ return vec_perm(val, val, vByteSwap);
+}
+# endif
+
+/* Power8 Crypto gives us vpermxor which is very handy for
+ * PPC64EB.
+ *
+ * U8x16 vpermxor(U8x16 a, U8x16 b, U8x16 mask)
+ * {
+ * U8x16 ret;
+ * for (int i = 0; i < 16; i++) {
+ * ret[i] = a[mask[i] & 0xF] ^ b[mask[i] >> 4];
+ * }
+ * return ret;
+ * }
+ *
+ * Because both of the main loops load the key, swap, and xor it with data,
+ * we can combine the key swap into this instruction.
+ */
+# ifdef vec_permxor
+# define XXH_vec_permxor vec_permxor
+# else
+# define XXH_vec_permxor __builtin_crypto_vpermxor
+# endif
+#endif
+/*
+ * Because we reinterpret the multiply, there are endian memes: vec_mulo actually becomes
+ * vec_mule.
+ *
+ * Additionally, the intrinsic wasn't added until GCC 8, despite existing for a while.
+ * Clang has an easy way to control this, we can just use the builtin which doesn't swap.
+ * GCC needs inline assembly. */
+#if __has_builtin(__builtin_altivec_vmuleuw)
+# define XXH_vec_mulo __builtin_altivec_vmulouw
+# define XXH_vec_mule __builtin_altivec_vmuleuw
+#else
+/* Adapted from https://github.com/google/highwayhash/blob/master/highwayhash/hh_vsx.h. */
+XXH_FORCE_INLINE U64x2 XXH_vec_mulo(U32x4 a, U32x4 b) {
+ U64x2 result;
+ __asm__("vmulouw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
+ return result;
+}
+XXH_FORCE_INLINE U64x2 XXH_vec_mule(U32x4 a, U32x4 b) {
+ U64x2 result;
+ __asm__("vmuleuw %0, %1, %2" : "=v" (result) : "v" (a), "v" (b));
+ return result;
+}
+#endif
+#endif
+
+
+/* ==========================================
+ * XXH3 default settings
+ * ========================================== */
+
+#define XXH_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */
+
+#if (XXH_SECRET_DEFAULT_SIZE < XXH3_SECRET_SIZE_MIN)
+# error "default keyset is not large enough"
+#endif
+
+XXH_ALIGN(64) static const BYTE kSecret[XXH_SECRET_DEFAULT_SIZE] = {
+ 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c,
+ 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f,
+ 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21,
+ 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c,
+ 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3,
+ 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8,
+ 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d,
+ 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64,
+
+ 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb,
+ 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e,
+ 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce,
+ 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e,
+};
+
+
+static XXH128_hash_t
+XXH3_mul128(U64 ll1, U64 ll2)
+{
+/* __uint128_t seems a bad choice with emscripten current, see https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 */
+#if !defined(__wasm__) && defined(__SIZEOF_INT128__) || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
+
+ __uint128_t lll = (__uint128_t)ll1 * ll2;
+ XXH128_hash_t const r128 = { (U64)(lll), (U64)(lll >> 64) };
+ return r128;
+
+#elif defined(_M_X64) || defined(_M_IA64)
+
+#ifndef _MSC_VER
+# pragma intrinsic(_umul128)
+#endif
+ U64 llhigh;
+ U64 const lllow = _umul128(ll1, ll2, &llhigh);
+ XXH128_hash_t const r128 = { lllow, llhigh };
+ return r128;
+
+#else /* Portable scalar version */
+
+ /* emulate 64x64->128b multiplication, using four 32x32->64 */
+ U32 const h1 = (U32)(ll1 >> 32);
+ U32 const h2 = (U32)(ll2 >> 32);
+ U32 const l1 = (U32)ll1;
+ U32 const l2 = (U32)ll2;
+
+ U64 const llh = XXH_mult32to64(h1, h2);
+ U64 const llm1 = XXH_mult32to64(l1, h2);
+ U64 const llm2 = XXH_mult32to64(h1, l2);
+ U64 const lll = XXH_mult32to64(l1, l2);
+
+ U64 const t = lll + (llm1 << 32);
+ U64 const carry1 = t < lll;
+
+ U64 const lllow = t + (llm2 << 32);
+ U64 const carry2 = lllow < t;
+ U64 const llhigh = llh + (llm1 >> 32) + (llm2 >> 32) + carry1 + carry2;
+
+ XXH128_hash_t const r128 = { lllow, llhigh };
+ return r128;
+
+#endif
+}
+
+
+#if defined(__GNUC__) && defined(__i386__)
+/* GCC is stupid and tries to vectorize this.
+ * This tells GCC that it is wrong. */
+__attribute__((__target__("no-sse")))
+#endif
+static U64
+XXH3_mul128_fold64(U64 ll1, U64 ll2)
+{
+/* __uint128_t seems a bad choice with emscripten current, see https://github.com/Cyan4973/xxHash/issues/211#issuecomment-515575677 */
+#if !defined(__wasm__) && defined(__SIZEOF_INT128__) || (defined(_INTEGRAL_MAX_BITS) && _INTEGRAL_MAX_BITS >= 128)
+
+ __uint128_t lll = (__uint128_t)ll1 * ll2;
+ return (U64)lll ^ (U64)(lll >> 64);
+
+#elif defined(_M_X64) || defined(_M_IA64)
+
+#ifndef _MSC_VER
+# pragma intrinsic(_umul128)
+#endif
+ U64 llhigh;
+ U64 const lllow = _umul128(ll1, ll2, &llhigh);
+ return lllow ^ llhigh;
+
+ /* We have to do it out manually on 32-bit.
+ * This is a modified, unrolled, widened, and optimized version of the
+ * mulqdu routine from Hacker's Delight.
+ *
+ * https://www.hackersdelight.org/hdcodetxt/mulqdu.c.txt
+ *
+ * This was modified to use U32->U64 multiplication instead
+ * of U16->U32, to add the high and low values in the end,
+ * be endian-independent, and I added a partial assembly
+ * implementation for ARM. */
+
+ /* An easy 128-bit folding multiply on ARMv6T2 and ARMv7-A/R can be done with
+ * the mighty umaal (Unsigned Multiply Accumulate Accumulate Long) which takes 4 cycles
+ * or less, doing a long multiply and adding two 32-bit integers:
+ *
+ * void umaal(U32 *RdLo, U32 *RdHi, U32 Rn, U32 Rm)
+ * {
+ * U64 prodAcc = (U64)Rn * (U64)Rm;
+ * prodAcc += *RdLo;
+ * prodAcc += *RdHi;
+ * *RdLo = prodAcc & 0xFFFFFFFF;
+ * *RdHi = prodAcc >> 32;
+ * }
+ *
+ * This is compared to umlal which adds to a single 64-bit integer:
+ *
+ * void umlal(U32 *RdLo, U32 *RdHi, U32 Rn, U32 Rm)
+ * {
+ * U64 prodAcc = (U64)Rn * (U64)Rm;
+ * prodAcc += (*RdLo | ((U64)*RdHi << 32);
+ * *RdLo = prodAcc & 0xFFFFFFFF;
+ * *RdHi = prodAcc >> 32;
+ * }
+ *
+ * Getting the compiler to emit them is like pulling teeth, and checking
+ * for it is annoying because ARMv7-M lacks this instruction. However, it
+ * is worth it, because this is an otherwise expensive operation. */
+
+ /* GCC-compatible, ARMv6t2 or ARMv7+, non-M variant, and 32-bit */
+#elif defined(__GNUC__) /* GCC-compatible */ \
+ && defined(__ARM_ARCH) && !defined(__aarch64__) && !defined(__arm64__) /* 32-bit ARM */\
+ && !defined(__ARM_ARCH_7M__) /* <- Not ARMv7-M vv*/ \
+ && !(defined(__TARGET_ARCH_ARM) && __TARGET_ARCH_ARM == 0 && __TARGET_ARCH_THUMB == 4) \
+ && (defined(__ARM_ARCH_6T2__) || __ARM_ARCH > 6) /* ARMv6T2 or later */
+
+ U32 w[4] = { 0 };
+ U32 u[2] = { (U32)(ll1 >> 32), (U32)ll1 };
+ U32 v[2] = { (U32)(ll2 >> 32), (U32)ll2 };
+ U32 k;
+
+ /* U64 t = (U64)u[1] * (U64)v[1];
+ * w[3] = t & 0xFFFFFFFF;
+ * k = t >> 32; */
+ __asm__("umull %0, %1, %2, %3"
+ : "=r" (w[3]), "=r" (k)
+ : "r" (u[1]), "r" (v[1]));
+
+ /* t = (U64)u[0] * (U64)v[1] + w[2] + k;
+ * w[2] = t & 0xFFFFFFFF;
+ * k = t >> 32; */
+ __asm__("umaal %0, %1, %2, %3"
+ : "+r" (w[2]), "+r" (k)
+ : "r" (u[0]), "r" (v[1]));
+ w[1] = k;
+ k = 0;
+
+ /* t = (U64)u[1] * (U64)v[0] + w[2] + k;
+ * w[2] = t & 0xFFFFFFFF;
+ * k = t >> 32; */
+ __asm__("umaal %0, %1, %2, %3"
+ : "+r" (w[2]), "+r" (k)
+ : "r" (u[1]), "r" (v[0]));
+
+ /* t = (U64)u[0] * (U64)v[0] + w[1] + k;
+ * w[1] = t & 0xFFFFFFFF;
+ * k = t >> 32; */
+ __asm__("umaal %0, %1, %2, %3"
+ : "+r" (w[1]), "+r" (k)
+ : "r" (u[0]), "r" (v[0]));
+ w[0] = k;
+
+ return (w[1] | ((U64)w[0] << 32)) ^ (w[3] | ((U64)w[2] << 32));
+
+#else /* Portable scalar version */
+
+ /* emulate 64x64->128b multiplication, using four 32x32->64 */
+ U32 const h1 = (U32)(ll1 >> 32);
+ U32 const h2 = (U32)(ll2 >> 32);
+ U32 const l1 = (U32)ll1;
+ U32 const l2 = (U32)ll2;
+
+ U64 const llh = XXH_mult32to64(h1, h2);
+ U64 const llm1 = XXH_mult32to64(l1, h2);
+ U64 const llm2 = XXH_mult32to64(h1, l2);
+ U64 const lll = XXH_mult32to64(l1, l2);
+
+ U64 const t = lll + (llm1 << 32);
+ U64 const carry1 = t < lll;
+
+ U64 const lllow = t + (llm2 << 32);
+ U64 const carry2 = lllow < t;
+ U64 const llhigh = llh + (llm1 >> 32) + (llm2 >> 32) + carry1 + carry2;
+
+ return llhigh ^ lllow;
+
+#endif
+}
+
+
+static XXH64_hash_t XXH3_avalanche(U64 h64)
+{
+ h64 ^= h64 >> 37;
+ h64 *= PRIME64_3;
+ h64 ^= h64 >> 32;
+ return h64;
+}
+
+
+/* ==========================================
+ * Short keys
+ * ========================================== */
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_1to3_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed)
+{
+ XXH_ASSERT(data != NULL);
+ XXH_ASSERT(1 <= len && len <= 3);
+ XXH_ASSERT(keyPtr != NULL);
+ { BYTE const c1 = ((const BYTE*)data)[0];
+ BYTE const c2 = ((const BYTE*)data)[len >> 1];
+ BYTE const c3 = ((const BYTE*)data)[len - 1];
+ U32 const combined = ((U32)c1) + (((U32)c2) << 8) + (((U32)c3) << 16) + (((U32)len) << 24);
+ U64 const keyed = (U64)combined ^ (XXH_readLE32(keyPtr) + seed);
+ U64 const mixed = keyed * PRIME64_1;
+ return XXH3_avalanche(mixed);
+ }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_4to8_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed)
+{
+ XXH_ASSERT(data != NULL);
+ XXH_ASSERT(keyPtr != NULL);
+ XXH_ASSERT(4 <= len && len <= 8);
+ { U32 const in1 = XXH_readLE32(data);
+ U32 const in2 = XXH_readLE32((const BYTE*)data + len - 4);
+ U64 const in64 = in1 + ((U64)in2 << 32);
+ U64 const keyed = in64 ^ (XXH_readLE64(keyPtr) + seed);
+ U64 const mix64 = len + ((keyed ^ (keyed >> 51)) * PRIME32_1);
+ return XXH3_avalanche((mix64 ^ (mix64 >> 47)) * PRIME64_2);
+ }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_9to16_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed)
+{
+ XXH_ASSERT(data != NULL);
+ XXH_ASSERT(keyPtr != NULL);
+ XXH_ASSERT(9 <= len && len <= 16);
+ { const U64* const key64 = (const U64*) keyPtr;
+ U64 const ll1 = XXH_readLE64(data) ^ (XXH_readLE64(key64) + seed);
+ U64 const ll2 = XXH_readLE64((const BYTE*)data + len - 8) ^ (XXH_readLE64(key64+1) - seed);
+ U64 const acc = len + (ll1 + ll2) + XXH3_mul128_fold64(ll1, ll2);
+ return XXH3_avalanche(acc);
+ }
+}
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_0to16_64b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed)
+{
+ XXH_ASSERT(len <= 16);
+ { if (len > 8) return XXH3_len_9to16_64b(data, len, keyPtr, seed);
+ if (len >= 4) return XXH3_len_4to8_64b(data, len, keyPtr, seed);
+ if (len) return XXH3_len_1to3_64b(data, len, keyPtr, seed);
+ return 0;
+ }
+}
+
+
+/* === Long Keys === */
+
+#define STRIPE_LEN 64
+#define XXH_SECRET_CONSUME_RATE 8 /* nb of secret bytes consumed at each accumulation */
+#define ACC_NB (STRIPE_LEN / sizeof(U64))
+
+typedef enum { XXH3_acc_64bits, XXH3_acc_128bits } XXH3_accWidth_e;
+
+XXH_FORCE_INLINE void
+XXH3_accumulate_512( void* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT data,
+ const void* XXH_RESTRICT key,
+ XXH3_accWidth_e accWidth)
+{
+#if (XXH_VECTOR == XXH_AVX2)
+
+ XXH_ASSERT((((size_t)acc) & 31) == 0);
+ { XXH_ALIGN(32) __m256i* const xacc = (__m256i *) acc;
+ const __m256i* const xdata = (const __m256i *) data; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this type */
+ const __m256i* const xkey = (const __m256i *) key; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this type */
+
+ size_t i;
+ for (i=0; i < STRIPE_LEN/sizeof(__m256i); i++) {
+ __m256i const d = _mm256_loadu_si256 (xdata+i);
+ __m256i const k = _mm256_loadu_si256 (xkey+i);
+ __m256i const dk = _mm256_xor_si256 (d,k); /* uint32 dk[8] = {d0+k0, d1+k1, d2+k2, d3+k3, ...} */
+ __m256i const mul = _mm256_mul_epu32 (dk, _mm256_shuffle_epi32 (dk, 0x31)); /* uint64 mul[4] = {dk0*dk1, dk2*dk3, ...} */
+ if (accWidth == XXH3_acc_128bits) {
+ __m256i const dswap = _mm256_shuffle_epi32(d, _MM_SHUFFLE(1,0,3,2));
+ __m256i const add = _mm256_add_epi64(xacc[i], dswap);
+ xacc[i] = _mm256_add_epi64(mul, add);
+ } else { /* XXH3_acc_64bits */
+ __m256i const add = _mm256_add_epi64(xacc[i], d);
+ xacc[i] = _mm256_add_epi64(mul, add);
+ }
+ } }
+
+#elif (XXH_VECTOR == XXH_SSE2)
+
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+ { XXH_ALIGN(16) __m128i* const xacc = (__m128i *) acc; /* presumed */
+ const __m128i* const xdata = (const __m128i *) data; /* not really aligned, just for ptr arithmetic, and because _mm_loadu_si128() requires this type */
+ const __m128i* const xkey = (const __m128i *) key; /* not really aligned, just for ptr arithmetic, and because _mm_loadu_si128() requires this type */
+
+ size_t i;
+ for (i=0; i < STRIPE_LEN/sizeof(__m128i); i++) {
+ __m128i const d = _mm_loadu_si128 (xdata+i);
+ __m128i const k = _mm_loadu_si128 (xkey+i);
+ __m128i const dk = _mm_xor_si128 (d,k); /* uint32 dk[4] = {d0+k0, d1+k1, d2+k2, d3+k3} */
+ __m128i const mul = _mm_mul_epu32 (dk, _mm_shuffle_epi32 (dk, 0x31)); /* uint64 mul[2] = {dk0*dk1,dk2*dk3} */
+ if (accWidth == XXH3_acc_128bits) {
+ __m128i const dswap = _mm_shuffle_epi32(d, _MM_SHUFFLE(1,0,3,2));
+ __m128i const add = _mm_add_epi64(xacc[i], dswap);
+ xacc[i] = _mm_add_epi64(mul, add);
+ } else { /* XXH3_acc_64bits */
+ __m128i const add = _mm_add_epi64(xacc[i], d);
+ xacc[i] = _mm_add_epi64(mul, add);
+ }
+ } }
+
+#elif (XXH_VECTOR == XXH_NEON)
+
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+ {
+ XXH_ALIGN(16) uint64x2_t* const xacc = (uint64x2_t *) acc;
+ /* We don't use a uint32x4_t pointer because it causes bus errors on ARMv7. */
+ uint32_t const* const xdata = (const uint32_t *) data;
+ uint32_t const* const xkey = (const uint32_t *) key;
+
+ size_t i;
+ for (i=0; i < STRIPE_LEN / sizeof(uint64x2_t); i++) {
+#if !defined(__aarch64__) && !defined(__arm64__) && defined(__GNUC__) /* ARM32-specific hack */
+ /* vzip on ARMv7 Clang generates a lot of vmovs (technically vorrs) without this.
+ * vzip on 32-bit ARM NEON will overwrite the original register, and I think that Clang
+ * assumes I don't want to destroy it and tries to make a copy. This slows down the code
+ * a lot.
+ * aarch64 not only uses an entirely different syntax, but it requires three
+ * instructions...
+ * ext v1.16B, v0.16B, #8 // select high bits because aarch64 can't address them directly
+ * zip1 v3.2s, v0.2s, v1.2s // first zip
+ * zip2 v2.2s, v0.2s, v1.2s // second zip
+ * ...to do what ARM does in one:
+ * vzip.32 d0, d1 // Interleave high and low bits and overwrite. */
+
+ /* data_vec = xdata[i]; */
+ uint32x4_t const data_vec = vld1q_u32(xdata + (i * 4));
+ /* key_vec = xkey[i]; */
+ uint32x4_t const key_vec = vld1q_u32(xkey + (i * 4));
+ /* data_key = data_vec ^ key_vec; */
+ uint32x4_t data_key;
+
+ if (accWidth == XXH3_acc_64bits) {
+ /* Add first to prevent register swaps */
+ /* xacc[i] += data_vec; */
+ xacc[i] = vaddq_u64 (xacc[i], vreinterpretq_u64_u32(data_vec));
+ } else { /* XXH3_acc_128bits */
+ /* xacc[i] += swap(data_vec); */
+ /* can probably be optimized better */
+ uint64x2_t const data64 = vreinterpretq_u64_u32(data_vec);
+ uint64x2_t const swapped= vextq_u64(data64, data64, 1);
+ xacc[i] = vaddq_u64 (xacc[i], swapped);
+ }
+
+ data_key = veorq_u32(data_vec, key_vec);
+
+ /* Here's the magic. We use the quirkiness of vzip to shuffle data_key in place.
+ * shuffle: data_key[0, 1, 2, 3] = data_key[0, 2, 1, 3] */
+ __asm__("vzip.32 %e0, %f0" : "+w" (data_key));
+ /* xacc[i] += (uint64x2_t) data_key[0, 1] * (uint64x2_t) data_key[2, 3]; */
+ xacc[i] = vmlal_u32(xacc[i], vget_low_u32(data_key), vget_high_u32(data_key));
+
+#else
+ /* On aarch64, vshrn/vmovn seems to be equivalent to, if not faster than, the vzip method. */
+
+ /* data_vec = xdata[i]; */
+ uint32x4_t const data_vec = vld1q_u32(xdata + (i * 4));
+ /* key_vec = xkey[i]; */
+ uint32x4_t const key_vec = vld1q_u32(xkey + (i * 4));
+ /* data_key = data_vec ^ key_vec; */
+ uint32x4_t const data_key = veorq_u32(data_vec, key_vec);
+ /* data_key_lo = (uint32x2_t) (data_key & 0xFFFFFFFF); */
+ uint32x2_t const data_key_lo = vmovn_u64 (vreinterpretq_u64_u32(data_key));
+ /* data_key_hi = (uint32x2_t) (data_key >> 32); */
+ uint32x2_t const data_key_hi = vshrn_n_u64 (vreinterpretq_u64_u32(data_key), 32);
+ if (accWidth == XXH3_acc_64bits) {
+ /* xacc[i] += data_vec; */
+ xacc[i] = vaddq_u64 (xacc[i], vreinterpretq_u64_u32(data_vec));
+ } else { /* XXH3_acc_128bits */
+ /* xacc[i] += swap(data_vec); */
+ uint64x2_t const data64 = vreinterpretq_u64_u32(data_vec);
+ uint64x2_t const swapped= vextq_u64(data64, data64, 1);
+ xacc[i] = vaddq_u64 (xacc[i], swapped);
+ }
+ /* xacc[i] += (uint64x2_t) data_key_lo * (uint64x2_t) data_key_hi; */
+ xacc[i] = vmlal_u32 (xacc[i], data_key_lo, data_key_hi);
+
+#endif
+ }
+ }
+
+#elif (XXH_VECTOR == XXH_VSX)
+ U64x2* const xacc = (U64x2*) acc; /* presumed aligned */
+ U64x2 const* const xdata = (U64x2 const*) data; /* no alignment restriction */
+ U64x2 const* const xkey = (U64x2 const*) key; /* no alignment restriction */
+ U64x2 const v32 = { 32, 32 };
+#if XXH_VSX_BE
+ U8x16 const vXorSwap = { 0x07, 0x16, 0x25, 0x34, 0x43, 0x52, 0x61, 0x70,
+ 0x8F, 0x9E, 0xAD, 0xBC, 0xCB, 0xDA, 0xE9, 0xF8 };
+#endif
+ size_t i;
+ for (i = 0; i < STRIPE_LEN / sizeof(U64x2); i++) {
+ /* data_vec = xdata[i]; */
+ /* key_vec = xkey[i]; */
+#if XXH_VSX_BE
+ /* byteswap */
+ U64x2 const data_vec = XXH_vec_revb(vec_vsx_ld(0, xdata + i));
+ U64x2 const key_raw = vec_vsx_ld(0, xkey + i);
+ /* See comment above. data_key = data_vec ^ swap(xkey[i]); */
+ U64x2 const data_key = (U64x2)XXH_vec_permxor((U8x16)data_vec, (U8x16)key_raw, vXorSwap);
+#else
+ U64x2 const data_vec = vec_vsx_ld(0, xdata + i);
+ U64x2 const key_vec = vec_vsx_ld(0, xkey + i);
+ U64x2 const data_key = data_vec ^ key_vec;
+#endif
+ /* shuffled = (data_key << 32) | (data_key >> 32); */
+ U32x4 const shuffled = (U32x4)vec_rl(data_key, v32);
+ /* product = ((U64x2)data_key & 0xFFFFFFFF) * ((U64x2)shuffled & 0xFFFFFFFF); */
+ U64x2 const product = XXH_vec_mulo((U32x4)data_key, shuffled);
+ xacc[i] += product;
+
+ if (accWidth == XXH3_acc_64bits) {
+ xacc[i] += data_vec;
+ } else { /* XXH3_acc_128bits */
+ /* swap high and low halves */
+ U64x2 const data_swapped = vec_xxpermdi(data_vec, data_vec, 2);
+ xacc[i] += data_swapped;
+ }
+ }
+
+#else /* scalar variant of Accumulator - universal */
+
+ XXH_ALIGN(XXH_ACC_ALIGN) U64* const xacc = (U64*) acc; /* presumed aligned on 32-bytes boundaries, little hint for the auto-vectorizer */
+ const char* const xdata = (const char*) data; /* no alignment restriction */
+ const char* const xkey = (const char*) key; /* no alignment restriction */
+ size_t i;
+ XXH_ASSERT(((size_t)acc & (XXH_ACC_ALIGN-1)) == 0);
+ for (i=0; i < ACC_NB; i+=2) {
+ U64 const in1 = XXH_readLE64(xdata + 8*i);
+ U64 const in2 = XXH_readLE64(xdata + 8*(i+1));
+ U64 const key1 = XXH_readLE64(xkey + 8*i);
+ U64 const key2 = XXH_readLE64(xkey + 8*(i+1));
+ U64 const data_key1 = key1 ^ in1;
+ U64 const data_key2 = key2 ^ in2;
+ xacc[i] += XXH_mult32to64(data_key1 & 0xFFFFFFFF, data_key1 >> 32);
+ xacc[i+1] += XXH_mult32to64(data_key2 & 0xFFFFFFFF, data_key2 >> 32);
+ if (accWidth == XXH3_acc_128bits) {
+ xacc[i] += in2;
+ xacc[i+1] += in1;
+ } else { /* XXH3_acc_64bits */
+ xacc[i] += in1;
+ xacc[i+1] += in2;
+ }
+ }
+#endif
+}
+
+XXH_FORCE_INLINE void
+XXH3_scrambleAcc(void* XXH_RESTRICT acc, const void* XXH_RESTRICT key)
+{
+#if (XXH_VECTOR == XXH_AVX2)
+
+ XXH_ASSERT((((size_t)acc) & 31) == 0);
+ { XXH_ALIGN(32) __m256i* const xacc = (__m256i*) acc;
+ const __m256i* const xkey = (const __m256i *) key; /* not really aligned, just for ptr arithmetic, and because _mm256_loadu_si256() requires this argument type */
+ const __m256i prime32 = _mm256_set1_epi32((int)PRIME32_1);
+
+ size_t i;
+ for (i=0; i < STRIPE_LEN/sizeof(__m256i); i++) {
+ __m256i data = xacc[i];
+ __m256i const shifted = _mm256_srli_epi64(data, 47);
+ data = _mm256_xor_si256(data, shifted);
+
+ { __m256i const k = _mm256_loadu_si256 (xkey+i);
+ __m256i const dk = _mm256_xor_si256 (data, k);
+
+ __m256i const dk1 = _mm256_mul_epu32 (dk, prime32);
+
+ __m256i const d2 = _mm256_shuffle_epi32 (dk, 0x31);
+ __m256i const dk2 = _mm256_mul_epu32 (d2, prime32);
+ __m256i const dk2h= _mm256_slli_epi64 (dk2, 32);
+
+ xacc[i] = _mm256_add_epi64(dk1, dk2h);
+ } }
+ }
+
+#elif (XXH_VECTOR == XXH_SSE2)
+
+ { XXH_ALIGN(16) __m128i* const xacc = (__m128i*) acc;
+ const __m128i* const xkey = (const __m128i *) key; /* not really aligned, just for ptr arithmetic */
+ const __m128i prime32 = _mm_set1_epi32((int)PRIME32_1);
+
+ size_t i;
+ for (i=0; i < STRIPE_LEN/sizeof(__m128i); i++) {
+ __m128i data = xacc[i];
+ __m128i const shifted = _mm_srli_epi64(data, 47);
+ data = _mm_xor_si128(data, shifted);
+
+ { __m128i const k = _mm_loadu_si128 (xkey+i);
+ __m128i const dk = _mm_xor_si128 (data,k);
+
+ __m128i const dk1 = _mm_mul_epu32 (dk, prime32);
+
+ __m128i const d2 = _mm_shuffle_epi32 (dk, 0x31);
+ __m128i const dk2 = _mm_mul_epu32 (d2, prime32);
+ __m128i const dk2h= _mm_slli_epi64(dk2, 32);
+
+ xacc[i] = _mm_add_epi64(dk1, dk2h);
+ } }
+ }
+
+#elif (XXH_VECTOR == XXH_NEON)
+
+ XXH_ASSERT((((size_t)acc) & 15) == 0);
+
+ { uint64x2_t* const xacc = (uint64x2_t*) acc;
+ uint32_t const* const xkey = (uint32_t const*) key;
+ uint32x2_t const prime = vdup_n_u32 (PRIME32_1);
+
+ size_t i;
+ for (i=0; i < STRIPE_LEN/sizeof(uint64x2_t); i++) {
+ /* data_vec = xacc[i] ^ (xacc[i] >> 47); */
+ uint64x2_t const acc_vec = xacc[i];
+ uint64x2_t const shifted = vshrq_n_u64 (acc_vec, 47);
+ uint64x2_t const data_vec = veorq_u64 (acc_vec, shifted);
+
+ /* key_vec = xkey[i]; */
+ uint32x4_t const key_vec = vld1q_u32 (xkey + (i * 4));
+ /* data_key = data_vec ^ key_vec; */
+ uint32x4_t const data_key = veorq_u32 (vreinterpretq_u32_u64(data_vec), key_vec);
+ /* shuffled = { data_key[0, 2], data_key[1, 3] }; */
+ uint32x2x2_t const shuffled = vzip_u32 (vget_low_u32(data_key), vget_high_u32(data_key));
+
+ /* data_key *= PRIME32_1 */
+
+ /* prod_hi = (data_key >> 32) * PRIME32_1; */
+ uint64x2_t const prod_hi = vmull_u32 (shuffled.val[1], prime);
+ /* xacc[i] = prod_hi << 32; */
+ xacc[i] = vshlq_n_u64(prod_hi, 32);
+ /* xacc[i] += (prod_hi & 0xFFFFFFFF) * PRIME32_1; */
+ xacc[i] = vmlal_u32(xacc[i], shuffled.val[0], prime);
+ } }
+
+#elif (XXH_VECTOR == XXH_VSX)
+
+ U64x2* const xacc = (U64x2*) acc;
+ const U64x2* const xkey = (const U64x2*) key;
+ /* constants */
+ U64x2 const v32 = { 32, 32 };
+ U64x2 const v47 = { 47, 47 };
+ U32x4 const prime = { PRIME32_1, PRIME32_1, PRIME32_1, PRIME32_1 };
+ size_t i;
+#if XXH_VSX_BE
+ /* endian swap */
+ U8x16 const vXorSwap = { 0x07, 0x16, 0x25, 0x34, 0x43, 0x52, 0x61, 0x70,
+ 0x8F, 0x9E, 0xAD, 0xBC, 0xCB, 0xDA, 0xE9, 0xF8 };
+#endif
+ for (i = 0; i < STRIPE_LEN / sizeof(U64x2); i++) {
+ U64x2 const acc_vec = xacc[i];
+ U64x2 const data_vec = acc_vec ^ (acc_vec >> v47);
+ /* key_vec = xkey[i]; */
+#if XXH_VSX_BE
+ /* swap bytes words */
+ U64x2 const key_raw = vec_vsx_ld(0, xkey + i);
+ U64x2 const data_key = (U64x2)XXH_vec_permxor((U8x16)data_vec, (U8x16)key_raw, vXorSwap);
+#else
+ U64x2 const key_vec = vec_vsx_ld(0, xkey + i);
+ U64x2 const data_key = data_vec ^ key_vec;
+#endif
+
+ /* data_key *= PRIME32_1 */
+
+ /* prod_lo = ((U64x2)data_key & 0xFFFFFFFF) * ((U64x2)prime & 0xFFFFFFFF); */
+ U64x2 const prod_even = XXH_vec_mule((U32x4)data_key, prime);
+ /* prod_hi = ((U64x2)data_key >> 32) * ((U64x2)prime >> 32); */
+ U64x2 const prod_odd = XXH_vec_mulo((U32x4)data_key, prime);
+ xacc[i] = prod_odd + (prod_even << v32);
+ }
+
+#else /* scalar variant of Scrambler - universal */
+
+ XXH_ALIGN(XXH_ACC_ALIGN) U64* const xacc = (U64*) acc; /* presumed aligned on 32-bytes boundaries, little hint for the auto-vectorizer */
+ const char* const xkey = (const char*) key; /* no alignment restriction */
+ int i;
+ XXH_ASSERT((((size_t)acc) & (XXH_ACC_ALIGN-1)) == 0);
+
+ for (i=0; i < (int)ACC_NB; i++) {
+ U64 const key64 = XXH_readLE64(xkey + 8*i);
+ U64 acc64 = xacc[i];
+ acc64 ^= acc64 >> 47;
+ acc64 ^= key64;
+ acc64 *= PRIME32_1;
+ xacc[i] = acc64;
+ }
+
+#endif
+}
+
+/* assumption : nbStripes will not overflow secret size */
+XXH_FORCE_INLINE void
+XXH3_accumulate( U64* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT data,
+ const void* XXH_RESTRICT secret,
+ size_t nbStripes,
+ XXH3_accWidth_e accWidth)
+{
+ size_t n;
+ /* Clang doesn't unroll this loop without the pragma. Unrolling can be up to 1.4x faster.
+ * The unroll statement seems detrimental for WASM (@aras-p) and ARM though.
+ */
+#if defined(__clang__) && !defined(__OPTIMIZE_SIZE__) && !defined(__ARM_ARCH) && !defined(__EMSCRIPTEN__)
+# pragma clang loop unroll(enable)
+#endif
+
+ for (n = 0; n < nbStripes; n++ ) {
+ XXH3_accumulate_512(acc,
+ (const char*)data + n*STRIPE_LEN,
+ (const char*)secret + n*XXH_SECRET_CONSUME_RATE,
+ accWidth);
+ }
+}
+
+/* note : clang auto-vectorizes well in SS2 mode _if_ this function is `static`,
+ * and doesn't auto-vectorize it at all if it is `FORCE_INLINE`.
+ * However, it auto-vectorizes better AVX2 if it is `FORCE_INLINE`
+ * Pretty much every other modes and compilers prefer `FORCE_INLINE`.
+ */
+#if defined(__clang__) && (XXH_VECTOR==0) && !defined(__AVX2__)
+static void
+#else
+XXH_FORCE_INLINE void
+#endif
+XXH3_hashLong_internal_loop( U64* XXH_RESTRICT acc,
+ const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize,
+ XXH3_accWidth_e accWidth)
+{
+ size_t const nb_rounds = (secretSize - STRIPE_LEN) / XXH_SECRET_CONSUME_RATE;
+ size_t const block_len = STRIPE_LEN * nb_rounds;
+ size_t const nb_blocks = len / block_len;
+
+ size_t n;
+
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+
+ for (n = 0; n < nb_blocks; n++) {
+ XXH3_accumulate(acc, (const char*)data + n*block_len, secret, nb_rounds, accWidth);
+ XXH3_scrambleAcc(acc, (const char*)secret + secretSize - STRIPE_LEN);
+ }
+
+ /* last partial block */
+ XXH_ASSERT(len > STRIPE_LEN);
+ { size_t const nbStripes = (len - (block_len * nb_blocks)) / STRIPE_LEN;
+ XXH_ASSERT(nbStripes <= (secretSize / XXH_SECRET_CONSUME_RATE));
+ XXH3_accumulate(acc, (const char*)data + nb_blocks*block_len, secret, nbStripes, accWidth);
+
+ /* last stripe */
+ if (len & (STRIPE_LEN - 1)) {
+ const void* const p = (const char*)data + len - STRIPE_LEN;
+#define XXH_SECRET_LASTACC_START 7 /* do not align on 8, so that secret is different from scrambler */
+ XXH3_accumulate_512(acc, p, (const char*)secret + secretSize - STRIPE_LEN - XXH_SECRET_LASTACC_START, accWidth);
+ } }
+}
+
+XXH_FORCE_INLINE U64
+XXH3_mix2Accs(const U64* XXH_RESTRICT acc, const void* XXH_RESTRICT secret)
+{
+ const U64* const key64 = (const U64*)secret;
+ return XXH3_mul128_fold64(
+ acc[0] ^ XXH_readLE64(key64),
+ acc[1] ^ XXH_readLE64(key64+1) );
+}
+
+static XXH64_hash_t
+XXH3_mergeAccs(const U64* XXH_RESTRICT acc, const void* XXH_RESTRICT secret, U64 start)
+{
+ U64 result64 = start;
+
+ result64 += XXH3_mix2Accs(acc+0, (const char*)secret + 0);
+ result64 += XXH3_mix2Accs(acc+2, (const char*)secret + 16);
+ result64 += XXH3_mix2Accs(acc+4, (const char*)secret + 32);
+ result64 += XXH3_mix2Accs(acc+6, (const char*)secret + 48);
+
+ return XXH3_avalanche(result64);
+}
+
+#define XXH3_INIT_ACC { PRIME32_3, PRIME64_1, PRIME64_2, PRIME64_3, \
+ PRIME64_4, PRIME32_2, PRIME64_5, PRIME32_1 };
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_hashLong_internal(const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize)
+{
+ XXH_ALIGN(XXH_ACC_ALIGN) U64 acc[ACC_NB] = XXH3_INIT_ACC;
+
+ XXH3_hashLong_internal_loop(acc, data, len, secret, secretSize, XXH3_acc_64bits);
+
+ /* converge into final hash */
+ XXH_STATIC_ASSERT(sizeof(acc) == 64);
+#define XXH_SECRET_MERGEACCS_START 11 /* do not align on 8, so that secret is different from accumulator */
+ XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+ return XXH3_mergeAccs(acc, (const char*)secret + XXH_SECRET_MERGEACCS_START, (U64)len * PRIME64_1);
+}
+
+
+XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */
+XXH3_hashLong_64b_defaultSecret(const void* XXH_RESTRICT data, size_t len)
+{
+ return XXH3_hashLong_internal(data, len, kSecret, sizeof(kSecret));
+}
+
+XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */
+XXH3_hashLong_64b_withSecret(const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize)
+{
+ return XXH3_hashLong_internal(data, len, secret, secretSize);
+}
+
+
+XXH_FORCE_INLINE void XXH_writeLE64(void* dst, U64 v64)
+{
+ if (!XXH_CPU_LITTLE_ENDIAN) v64 = XXH_swap64(v64);
+ memcpy(dst, &v64, sizeof(v64));
+}
+
+/* XXH3_initKeySeed() :
+ * destination `customSecret` is presumed allocated and same size as `kSecret`.
+ */
+XXH_FORCE_INLINE void XXH3_initKeySeed(void* customSecret, U64 seed64)
+{
+ char* const dst = (char*)customSecret;
+ const char* const src = (const char*)kSecret;
+ int const nbRounds = XXH_SECRET_DEFAULT_SIZE / 16;
+ int i;
+
+ XXH_STATIC_ASSERT((XXH_SECRET_DEFAULT_SIZE & 15) == 0);
+
+ for (i=0; i < nbRounds; i++) {
+ XXH_writeLE64(dst + 16*i, XXH_readLE64(src + 16*i) + seed64);
+ XXH_writeLE64(dst + 16*i + 8, XXH_readLE64(src + 16*i + 8) - seed64);
+ }
+}
+
+
+/* XXH3_hashLong_64b_withSeed() :
+ * Generate a custom key,
+ * based on alteration of default kSecret with the seed,
+ * and then use this key for long mode hashing.
+ * This operation is decently fast but nonetheless costs a little bit of time.
+ * Try to avoid it whenever possible (typically when seed==0).
+ */
+XXH_NO_INLINE XXH64_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */
+XXH3_hashLong_64b_withSeed(const void* data, size_t len, XXH64_hash_t seed)
+{
+ XXH_ALIGN(8) char secret[XXH_SECRET_DEFAULT_SIZE];
+ if (seed==0) return XXH3_hashLong_64b_defaultSecret(data, len);
+ XXH3_initKeySeed(secret, seed);
+ return XXH3_hashLong_internal(data, len, secret, sizeof(secret));
+}
+
+
+XXH_FORCE_INLINE U64 XXH3_mix16B(const void* XXH_RESTRICT data,
+ const void* XXH_RESTRICT key, U64 seed64)
+{
+ const U64* const key64 = (const U64*)key;
+ U64 const ll1 = XXH_readLE64(data);
+ U64 const ll2 = XXH_readLE64((const BYTE*)data+8);
+ return XXH3_mul128_fold64(
+ ll1 ^ (XXH_readLE64(key64) + seed64),
+ ll2 ^ (XXH_readLE64(key64+1) - seed64) );
+}
+
+
+XXH_FORCE_INLINE XXH64_hash_t
+XXH3_len_17to128_64b(const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ const BYTE* const p = (const BYTE*)data;
+ const char* const key = (const char*)secret;
+
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(16 < len && len <= 128);
+
+ { U64 acc = len * PRIME64_1;
+ if (len > 32) {
+ if (len > 64) {
+ if (len > 96) {
+ acc += XXH3_mix16B(p+48, key+96, seed);
+ acc += XXH3_mix16B(p+len-64, key+112, seed);
+ }
+ acc += XXH3_mix16B(p+32, key+64, seed);
+ acc += XXH3_mix16B(p+len-48, key+80, seed);
+ }
+ acc += XXH3_mix16B(p+16, key+32, seed);
+ acc += XXH3_mix16B(p+len-32, key+48, seed);
+ }
+ acc += XXH3_mix16B(p+0, key+0, seed);
+ acc += XXH3_mix16B(p+len-16, key+16, seed);
+
+ return XXH3_avalanche(acc);
+ }
+}
+
+#define XXH3_MIDSIZE_MAX 240
+
+XXH_NO_INLINE XXH64_hash_t
+XXH3_len_129to240_64b(const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ const BYTE* const p = (const BYTE*)data;
+ const char* const key = (const char*)secret;
+
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
+
+ #define XXH3_MIDSIZE_STARTOFFSET 3
+ #define XXH3_MIDSIZE_LASTOFFSET 17
+
+ { U64 acc = len * PRIME64_1;
+ int const nbRounds = (int)len / 16;
+ int i;
+ for (i=0; i<8; i++) {
+ acc += XXH3_mix16B(p+(16*i), key+(16*i), seed);
+ }
+ acc = XXH3_avalanche(acc);
+ XXH_ASSERT(nbRounds >= 8);
+ for (i=8 ; i < nbRounds; i++) {
+ acc += XXH3_mix16B(p+(16*i), key+(16*(i-8)) + XXH3_MIDSIZE_STARTOFFSET, seed);
+ }
+ /* last bytes */
+ acc += XXH3_mix16B(p + len - 16, key + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET, seed);
+ return XXH3_avalanche(acc);
+ }
+}
+
+/* === Public entry point === */
+
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len)
+{
+ if (len <= 16) return XXH3_len_0to16_64b(data, len, kSecret, 0);
+ if (len <= 128) return XXH3_len_17to128_64b(data, len, kSecret, sizeof(kSecret), 0);
+ if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b(data, len, kSecret, sizeof(kSecret), 0);
+ return XXH3_hashLong_64b_defaultSecret(data, len);
+}
+
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize)
+{
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+ /* if an action must be taken should `secret` conditions not be respected,
+ * it should be done here.
+ * For now, it's a contract pre-condition.
+ * Adding a check and a branch here would cost performance at every hash */
+ if (len <= 16) return XXH3_len_0to16_64b(data, len, secret, 0);
+ if (len <= 128) return XXH3_len_17to128_64b(data, len, secret, secretSize, 0);
+ if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b(data, len, secret, secretSize, 0);
+ return XXH3_hashLong_64b_withSecret(data, len, secret, secretSize);
+}
+
+XXH_PUBLIC_API XXH64_hash_t
+XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed)
+{
+ if (len <= 16) return XXH3_len_0to16_64b(data, len, kSecret, seed);
+ if (len <= 128) return XXH3_len_17to128_64b(data, len, kSecret, sizeof(kSecret), seed);
+ if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_64b(data, len, kSecret, sizeof(kSecret), seed);
+ return XXH3_hashLong_64b_withSeed(data, len, seed);
+}
+
+/* === XXH3 streaming === */
+
+XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void)
+{
+ return (XXH3_state_t*)XXH_malloc(sizeof(XXH3_state_t));
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API void
+XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state)
+{
+ memcpy(dst_state, src_state, sizeof(*dst_state));
+}
+
+static void
+XXH3_64bits_reset_internal(XXH3_state_t* statePtr,
+ XXH64_hash_t seed,
+ const void* secret, size_t secretSize)
+{
+ XXH_ASSERT(statePtr != NULL);
+ memset(statePtr, 0, sizeof(*statePtr));
+ statePtr->acc[0] = PRIME32_3;
+ statePtr->acc[1] = PRIME64_1;
+ statePtr->acc[2] = PRIME64_2;
+ statePtr->acc[3] = PRIME64_3;
+ statePtr->acc[4] = PRIME64_4;
+ statePtr->acc[5] = PRIME32_2;
+ statePtr->acc[6] = PRIME64_5;
+ statePtr->acc[7] = PRIME32_1;
+ statePtr->seed = seed;
+ XXH_ASSERT(secret != NULL);
+ statePtr->secret = secret;
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+ statePtr->secretLimit = (XXH32_hash_t)(secretSize - STRIPE_LEN);
+ statePtr->nbStripesPerBlock = statePtr->secretLimit / XXH_SECRET_CONSUME_RATE;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset(XXH3_state_t* statePtr)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_64bits_reset_internal(statePtr, 0, kSecret, XXH_SECRET_DEFAULT_SIZE);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_64bits_reset_internal(statePtr, 0, secret, secretSize);
+ if (secret == NULL) return XXH_ERROR;
+ if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_64bits_reset_internal(statePtr, seed, kSecret, XXH_SECRET_DEFAULT_SIZE);
+ XXH3_initKeySeed(statePtr->customSecret, seed);
+ statePtr->secret = statePtr->customSecret;
+ return XXH_OK;
+}
+
+XXH_FORCE_INLINE void
+XXH3_consumeStripes( U64* acc,
+ XXH32_hash_t* nbStripesSoFarPtr, XXH32_hash_t nbStripesPerBlock,
+ const void* data, size_t totalStripes,
+ const void* secret, size_t secretLimit,
+ XXH3_accWidth_e accWidth)
+{
+ XXH_ASSERT(*nbStripesSoFarPtr < nbStripesPerBlock);
+ if (nbStripesPerBlock - *nbStripesSoFarPtr <= totalStripes) {
+ /* need a scrambling operation */
+ size_t const nbStripes = nbStripesPerBlock - *nbStripesSoFarPtr;
+ XXH3_accumulate(acc, data, (const char*)secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, nbStripes, accWidth);
+ XXH3_scrambleAcc(acc, (const char*)secret + secretLimit);
+ XXH3_accumulate(acc, (const char*)data + nbStripes * STRIPE_LEN, secret, totalStripes - nbStripes, accWidth);
+ *nbStripesSoFarPtr = (XXH32_hash_t)(totalStripes - nbStripes);
+ } else {
+ XXH3_accumulate(acc, data, (const char*)secret + nbStripesSoFarPtr[0] * XXH_SECRET_CONSUME_RATE, totalStripes, accWidth);
+ *nbStripesSoFarPtr += (XXH32_hash_t)totalStripes;
+ }
+}
+
+XXH_FORCE_INLINE XXH_errorcode
+XXH3_update(XXH3_state_t* state, const void* input, size_t len, XXH3_accWidth_e accWidth)
+{
+ if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ return XXH_OK;
+#else
+ return XXH_ERROR;
+#endif
+
+ { const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+ state->totalLen += len;
+
+ if (state->bufferedSize + len <= XXH3_INTERNALBUFFER_SIZE) { /* fill in tmp buffer */
+ XXH_memcpy(state->buffer + state->bufferedSize, input, len);
+ state->bufferedSize += (XXH32_hash_t)len;
+ return XXH_OK;
+ }
+ /* input now > XXH3_INTERNALBUFFER_SIZE */
+
+ #define XXH3_INTERNALBUFFER_STRIPES (XXH3_INTERNALBUFFER_SIZE / STRIPE_LEN)
+ XXH_STATIC_ASSERT(XXH3_INTERNALBUFFER_SIZE % STRIPE_LEN == 0); /* clean multiple */
+
+ if (state->bufferedSize) { /* some data within internal buffer: fill then consume it */
+ size_t const loadSize = XXH3_INTERNALBUFFER_SIZE - state->bufferedSize;
+ XXH_memcpy(state->buffer + state->bufferedSize, input, loadSize);
+ p += loadSize;
+ XXH3_consumeStripes(state->acc,
+ &state->nbStripesSoFar, state->nbStripesPerBlock,
+ state->buffer, XXH3_INTERNALBUFFER_STRIPES,
+ state->secret, state->secretLimit,
+ accWidth);
+ state->bufferedSize = 0;
+ }
+
+ /* consume input by full buffer quantities */
+ if (p+XXH3_INTERNALBUFFER_SIZE <= bEnd) {
+ const BYTE* const limit = bEnd - XXH3_INTERNALBUFFER_SIZE;
+ do {
+ XXH3_consumeStripes(state->acc,
+ &state->nbStripesSoFar, state->nbStripesPerBlock,
+ p, XXH3_INTERNALBUFFER_STRIPES,
+ state->secret, state->secretLimit,
+ accWidth);
+ p += XXH3_INTERNALBUFFER_SIZE;
+ } while (p<=limit);
+ }
+
+ if (p < bEnd) { /* some remaining input data : buffer it */
+ XXH_memcpy(state->buffer, p, (size_t)(bEnd-p));
+ state->bufferedSize = (XXH32_hash_t)(bEnd-p);
+ }
+ }
+
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_64bits_update(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, input, len, XXH3_acc_64bits);
+}
+
+
+XXH_FORCE_INLINE void
+XXH3_digest_long (XXH64_hash_t* acc, const XXH3_state_t* state, XXH3_accWidth_e accWidth)
+{
+ memcpy(acc, state->acc, sizeof(state->acc)); /* digest locally, state remains unaltered, and can continue ingesting more data afterwards */
+ if (state->bufferedSize >= STRIPE_LEN) {
+ size_t const totalNbStripes = state->bufferedSize / STRIPE_LEN;
+ XXH32_hash_t nbStripesSoFar = state->nbStripesSoFar;
+ XXH3_consumeStripes(acc,
+ &nbStripesSoFar, state->nbStripesPerBlock,
+ state->buffer, totalNbStripes,
+ state->secret, state->secretLimit,
+ accWidth);
+ if (state->bufferedSize % STRIPE_LEN) { /* one last partial stripe */
+ XXH3_accumulate_512(acc,
+ state->buffer + state->bufferedSize - STRIPE_LEN,
+ (const char*)state->secret + state->secretLimit - XXH_SECRET_LASTACC_START,
+ accWidth);
+ }
+ } else { /* bufferedSize < STRIPE_LEN */
+ if (state->bufferedSize) { /* one last stripe */
+ char lastStripe[STRIPE_LEN];
+ size_t const catchupSize = STRIPE_LEN - state->bufferedSize;
+ memcpy(lastStripe, (const char*)state->buffer + sizeof(state->buffer) - catchupSize, catchupSize);
+ memcpy(lastStripe + catchupSize, state->buffer, state->bufferedSize);
+ XXH3_accumulate_512(acc,
+ lastStripe,
+ (const char*)state->secret + state->secretLimit - XXH_SECRET_LASTACC_START,
+ accWidth);
+ } }
+}
+
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* state)
+{
+ if (state->totalLen > XXH3_MIDSIZE_MAX) {
+ XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[ACC_NB];
+ XXH3_digest_long(acc, state, XXH3_acc_64bits);
+ return XXH3_mergeAccs(acc, (const char*)state->secret + XXH_SECRET_MERGEACCS_START, (U64)state->totalLen * PRIME64_1);
+ }
+ /* len <= XXH3_MIDSIZE_MAX : short code */
+ if (state->seed)
+ return XXH3_64bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
+ return XXH3_64bits_withSecret(state->buffer, (size_t)(state->totalLen), state->secret, state->secretLimit + STRIPE_LEN);
+}
+
+/* ==========================================
+ * XXH3 128 bits (=> XXH128)
+ * ========================================== */
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_1to3_128b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed)
+{
+ XXH_ASSERT(data != NULL);
+ XXH_ASSERT(1 <= len && len <= 3);
+ XXH_ASSERT(keyPtr != NULL);
+ { const U32* const key32 = (const U32*) keyPtr;
+ BYTE const c1 = ((const BYTE*)data)[0];
+ BYTE const c2 = ((const BYTE*)data)[len >> 1];
+ BYTE const c3 = ((const BYTE*)data)[len - 1];
+ U32 const combinedl = ((U32)c1) + (((U32)c2) << 8) + (((U32)c3) << 16) + (((U32)len) << 24);
+ U32 const combinedh = XXH_swap32(combinedl);
+ U64 const keyedl = (U64)combinedl ^ (XXH_readLE32(key32) + seed);
+ U64 const keyedh = (U64)combinedh ^ (XXH_readLE32(key32+1) - seed);
+ U64 const mixedl = keyedl * PRIME64_1;
+ U64 const mixedh = keyedh * PRIME64_2;
+ XXH128_hash_t const h128 = { XXH3_avalanche(mixedl) /*low64*/, XXH3_avalanche(mixedh) /*high64*/ };
+ return h128;
+ }
+}
+
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_4to8_128b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed)
+{
+ XXH_ASSERT(data != NULL);
+ XXH_ASSERT(keyPtr != NULL);
+ XXH_ASSERT(4 <= len && len <= 8);
+ { U32 const in1 = XXH_readLE32(data);
+ U32 const in2 = XXH_readLE32((const BYTE*)data + len - 4);
+ U64 const in64l = in1 + ((U64)in2 << 32);
+ U64 const in64h = XXH_swap64(in64l);
+ U64 const keyedl = in64l ^ (XXH_readLE64(keyPtr) + seed);
+ U64 const keyedh = in64h ^ (XXH_readLE64((const char*)keyPtr + 8) - seed);
+ U64 const mix64l1 = len + ((keyedl ^ (keyedl >> 51)) * PRIME32_1);
+ U64 const mix64l2 = (mix64l1 ^ (mix64l1 >> 47)) * PRIME64_2;
+ U64 const mix64h1 = ((keyedh ^ (keyedh >> 47)) * PRIME64_1) - len;
+ U64 const mix64h2 = (mix64h1 ^ (mix64h1 >> 43)) * PRIME64_4;
+ { XXH128_hash_t const h128 = { XXH3_avalanche(mix64l2) /*low64*/, XXH3_avalanche(mix64h2) /*high64*/ };
+ return h128;
+ } }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_9to16_128b(const void* data, size_t len, const void* keyPtr, XXH64_hash_t seed)
+{
+ XXH_ASSERT(data != NULL);
+ XXH_ASSERT(keyPtr != NULL);
+ XXH_ASSERT(9 <= len && len <= 16);
+ { const U64* const key64 = (const U64*) keyPtr;
+ U64 const ll1 = XXH_readLE64(data) ^ (XXH_readLE64(key64) + seed);
+ U64 const ll2 = XXH_readLE64((const BYTE*)data + len - 8) ^ (XXH_readLE64(key64+1) - seed);
+ U64 const inlow = ll1 ^ ll2;
+ XXH128_hash_t m128 = XXH3_mul128(inlow, PRIME64_1);
+ m128.high64 += ll2 * PRIME64_1;
+ m128.low64 ^= (m128.high64 >> 32);
+ { XXH128_hash_t h128 = XXH3_mul128(m128.low64, PRIME64_2);
+ h128.high64 += m128.high64 * PRIME64_2;
+ h128.low64 = XXH3_avalanche(h128.low64);
+ h128.high64 = XXH3_avalanche(h128.high64);
+ return h128;
+ } }
+}
+
+/* Assumption : `secret` size is >= 16
+ * Note : it should be >= XXH3_SECRET_SIZE_MIN anyway */
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_0to16_128b(const void* data, size_t len, const void* secret, XXH64_hash_t seed)
+{
+ XXH_ASSERT(len <= 16);
+ { if (len > 8) return XXH3_len_9to16_128b(data, len, secret, seed);
+ if (len >= 4) return XXH3_len_4to8_128b(data, len, secret, seed);
+ if (len) return XXH3_len_1to3_128b(data, len, secret, seed);
+ { XXH128_hash_t const h128 = { 0, 0 };
+ return h128;
+ } }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_hashLong_128b_internal(const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize)
+{
+ XXH_ALIGN(XXH_ACC_ALIGN) U64 acc[ACC_NB] = XXH3_INIT_ACC;
+
+ XXH3_hashLong_internal_loop(acc, data, len, secret, secretSize, XXH3_acc_128bits);
+
+ /* converge into final hash */
+ XXH_STATIC_ASSERT(sizeof(acc) == 64);
+ XXH_ASSERT(secretSize >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+ { U64 const low64 = XXH3_mergeAccs(acc, (const char*)secret + XXH_SECRET_MERGEACCS_START, (U64)len * PRIME64_1);
+ U64 const high64 = XXH3_mergeAccs(acc, (const char*)secret + secretSize - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((U64)len * PRIME64_2));
+ XXH128_hash_t const h128 = { low64, high64 };
+ return h128;
+ }
+}
+
+XXH_NO_INLINE XXH128_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */
+XXH3_hashLong_128b_defaultSecret(const void* data, size_t len)
+{
+ return XXH3_hashLong_128b_internal(data, len, kSecret, sizeof(kSecret));
+}
+
+XXH_NO_INLINE XXH128_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */
+XXH3_hashLong_128b_withSecret(const void* data, size_t len,
+ const void* secret, size_t secretSize)
+{
+ return XXH3_hashLong_128b_internal(data, len, secret, secretSize);
+}
+
+XXH_NO_INLINE XXH128_hash_t /* It's important for performance that XXH3_hashLong is not inlined. Not sure why (uop cache maybe ?), but difference is large and easily measurable */
+XXH3_hashLong_128b_withSeed(const void* data, size_t len, XXH64_hash_t seed)
+{
+ XXH_ALIGN(8) char secret[XXH_SECRET_DEFAULT_SIZE];
+ if (seed == 0) return XXH3_hashLong_128b_defaultSecret(data, len);
+ XXH3_initKeySeed(secret, seed);
+ return XXH3_hashLong_128b_internal(data, len, secret, sizeof(secret));
+}
+
+XXH_NO_INLINE XXH128_hash_t
+XXH3_len_129to240_128b(const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ const BYTE* const p = (const BYTE*)data;
+ const char* const key = (const char*)secret;
+
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(128 < len && len <= XXH3_MIDSIZE_MAX);
+
+ { U64 acc1 = len * PRIME64_1;
+ U64 acc2 = 0;
+ int const nbRounds = (int)len / 32;
+ int i;
+ for (i=0; i<4; i++) {
+ acc1 += XXH3_mix16B(p+(32*i), key+(32*i), seed);
+ acc2 += XXH3_mix16B(p+(32*i)+16, key+(32*i)+16, 0ULL-seed);
+ }
+ acc1 = XXH3_avalanche(acc1);
+ acc2 = XXH3_avalanche(acc2);
+ XXH_ASSERT(nbRounds >= 4);
+ for (i=4 ; i < nbRounds; i++) {
+ acc1 += XXH3_mix16B(p+(32*i) , key+(32*(i-4)) + XXH3_MIDSIZE_STARTOFFSET, seed);
+ acc2 += XXH3_mix16B(p+(32*i)+16, key+(32*(i-4))+16 + XXH3_MIDSIZE_STARTOFFSET, 0ULL-seed);
+ }
+ /* last bytes */
+ acc1 += XXH3_mix16B(p + len - 16, key + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET , seed);
+ acc2 += XXH3_mix16B(p + len - 32, key + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, 0ULL-seed);
+
+ { U64 const low64 = acc1 + acc2;
+ U64 const high64 = (acc1 * PRIME64_1) + (acc2 * PRIME64_4) + ((len - seed) * PRIME64_2);
+ XXH128_hash_t const h128 = { XXH3_avalanche(low64), (XXH64_hash_t)0 - XXH3_avalanche(high64) };
+ return h128;
+ }
+ }
+}
+
+XXH_FORCE_INLINE XXH128_hash_t
+XXH3_len_17to128_128b(const void* XXH_RESTRICT data, size_t len,
+ const void* XXH_RESTRICT secret, size_t secretSize,
+ XXH64_hash_t seed)
+{
+ const BYTE* const p = (const BYTE*)data;
+ const char* const key = (const char*)secret;
+
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN); (void)secretSize;
+ XXH_ASSERT(16 < len && len <= 128);
+
+ { U64 acc1 = len * PRIME64_1;
+ U64 acc2 = 0;
+ if (len > 32) {
+ if (len > 64) {
+ if (len > 96) {
+ acc1 += XXH3_mix16B(p+48, key+96, seed);
+ acc2 += XXH3_mix16B(p+len-64, key+112, seed);
+ }
+ acc1 += XXH3_mix16B(p+32, key+64, seed);
+ acc2 += XXH3_mix16B(p+len-48, key+80, seed);
+ }
+ acc1 += XXH3_mix16B(p+16, key+32, seed);
+ acc2 += XXH3_mix16B(p+len-32, key+48, seed);
+ }
+ acc1 += XXH3_mix16B(p+0, key+0, seed);
+ acc2 += XXH3_mix16B(p+len-16, key+16, seed);
+
+ { U64 const low64 = acc1 + acc2;
+ U64 const high64 = (acc1 * PRIME64_1) + (acc2 * PRIME64_4) + ((len - seed) * PRIME64_2);
+ XXH128_hash_t const h128 = { XXH3_avalanche(low64), (XXH64_hash_t)0 - XXH3_avalanche(high64) };
+ return h128;
+ }
+ }
+}
+
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len)
+{
+ if (len <= 16) return XXH3_len_0to16_128b(data, len, kSecret, 0);
+ if (len <= 128) return XXH3_len_17to128_128b(data, len, kSecret, sizeof(kSecret), 0);
+ if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b(data, len, kSecret, sizeof(kSecret), 0);
+ return XXH3_hashLong_128b_defaultSecret(data, len);
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize)
+{
+ XXH_ASSERT(secretSize >= XXH3_SECRET_SIZE_MIN);
+ /* if an action must be taken should `secret` conditions not be respected,
+ * it should be done here.
+ * For now, it's a contract pre-condition.
+ * Adding a check and a branch here would cost performance at every hash */
+ if (len <= 16) return XXH3_len_0to16_128b(data, len, secret, 0);
+ if (len <= 128) return XXH3_len_17to128_128b(data, len, secret, secretSize, 0);
+ if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b(data, len, secret, secretSize, 0);
+ return XXH3_hashLong_128b_withSecret(data, len, secret, secretSize);
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed)
+{
+ if (len <= 16) return XXH3_len_0to16_128b(data, len, kSecret, seed);
+ if (len <= 128) return XXH3_len_17to128_128b(data, len, kSecret, sizeof(kSecret), seed);
+ if (len <= XXH3_MIDSIZE_MAX) return XXH3_len_129to240_128b(data, len, kSecret, sizeof(kSecret), seed);
+ return XXH3_hashLong_128b_withSeed(data, len, seed);
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH128(const void* data, size_t len, XXH64_hash_t seed)
+{
+ return XXH3_128bits_withSeed(data, len, seed);
+}
+
+
+/* === XXH3 128-bit streaming === */
+
+/* all the functions are actually the same as for 64-bit streaming variant,
+ just the reset one is different (different initial acc values for 0,5,6,7),
+ and near the end of the digest function */
+
+static void
+XXH3_128bits_reset_internal(XXH3_state_t* statePtr,
+ XXH64_hash_t seed,
+ const void* secret, size_t secretSize)
+{
+ XXH3_64bits_reset_internal(statePtr, seed, secret, secretSize);
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset(XXH3_state_t* statePtr)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_128bits_reset_internal(statePtr, 0, kSecret, XXH_SECRET_DEFAULT_SIZE);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_128bits_reset_internal(statePtr, 0, secret, secretSize);
+ if (secret == NULL) return XXH_ERROR;
+ if (secretSize < XXH3_SECRET_SIZE_MIN) return XXH_ERROR;
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed)
+{
+ if (statePtr == NULL) return XXH_ERROR;
+ XXH3_128bits_reset_internal(statePtr, seed, kSecret, XXH_SECRET_DEFAULT_SIZE);
+ XXH3_initKeySeed(statePtr->customSecret, seed);
+ statePtr->secret = statePtr->customSecret;
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH3_128bits_update(XXH3_state_t* state, const void* input, size_t len)
+{
+ return XXH3_update(state, input, len, XXH3_acc_128bits);
+}
+
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* state)
+{
+ if (state->totalLen > XXH3_MIDSIZE_MAX) {
+ XXH_ALIGN(XXH_ACC_ALIGN) XXH64_hash_t acc[ACC_NB];
+ XXH3_digest_long(acc, state, XXH3_acc_128bits);
+ XXH_ASSERT(state->secretLimit + STRIPE_LEN >= sizeof(acc) + XXH_SECRET_MERGEACCS_START);
+ { U64 const low64 = XXH3_mergeAccs(acc, (const char*)state->secret + XXH_SECRET_MERGEACCS_START, (U64)state->totalLen * PRIME64_1);
+ U64 const high64 = XXH3_mergeAccs(acc, (const char*)state->secret + state->secretLimit + STRIPE_LEN - sizeof(acc) - XXH_SECRET_MERGEACCS_START, ~((U64)state->totalLen * PRIME64_2));
+ XXH128_hash_t const h128 = { low64, high64 };
+ return h128;
+ }
+ }
+ /* len <= XXH3_MIDSIZE_MAX : short code */
+ if (state->seed)
+ return XXH3_128bits_withSeed(state->buffer, (size_t)state->totalLen, state->seed);
+ return XXH3_128bits_withSecret(state->buffer, (size_t)(state->totalLen), state->secret, state->secretLimit + STRIPE_LEN);
+}
+
+/* 128-bit utility functions */
+
+#include <string.h> /* memcmp */
+
+/* return : 1 is equal, 0 if different */
+XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2)
+{
+ /* note : XXH128_hash_t is compact, it has no padding byte */
+ return !(memcmp(&h1, &h2, sizeof(h1)));
+}
+
+/* This prototype is compatible with stdlib's qsort().
+ * return : >0 if *h128_1 > *h128_2
+ * <0 if *h128_1 < *h128_2
+ * =0 if *h128_1 == *h128_2 */
+XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2)
+{
+ XXH128_hash_t const h1 = *(const XXH128_hash_t*)h128_1;
+ XXH128_hash_t const h2 = *(const XXH128_hash_t*)h128_2;
+ int const hcmp = (h1.high64 > h2.high64) - (h2.high64 > h1.high64);
+ /* note : bets that, in most cases, hash values are different */
+ if (hcmp) return hcmp;
+ return (h1.low64 > h2.low64) - (h2.low64 > h1.low64);
+}
+
+
+/*====== Canonical representation ======*/
+XXH_PUBLIC_API void
+XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH128_canonical_t) == sizeof(XXH128_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) {
+ hash.high64 = XXH_swap64(hash.high64);
+ hash.low64 = XXH_swap64(hash.low64);
+ }
+ memcpy(dst, &hash.high64, sizeof(hash.high64));
+ memcpy((char*)dst + sizeof(hash.high64), &hash.low64, sizeof(hash.low64));
+}
+
+XXH_PUBLIC_API XXH128_hash_t
+XXH128_hashFromCanonical(const XXH128_canonical_t* src)
+{
+ XXH128_hash_t h;
+ h.high64 = XXH_readBE64(src);
+ h.low64 = XXH_readBE64(src->digest + 8);
+ return h;
+}
+
+
+
+#endif /* XXH3_H */
diff --git a/src/libs/decoders/xxhash.c b/src/libs/decoders/xxhash.c
new file mode 100644
index 000000000..0e847d0d0
--- /dev/null
+++ b/src/libs/decoders/xxhash.c
@@ -0,0 +1,1114 @@
+/*
+* xxHash - Fast Hash algorithm
+* Copyright (C) 2012-2016, Yann Collet
+*
+* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+*
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following disclaimer
+* in the documentation and/or other materials provided with the
+* distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+* You can contact the author at :
+* - xxHash homepage: http://www.xxhash.com
+* - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+
+/* *************************************
+* Tuning parameters
+***************************************/
+/*!XXH_FORCE_MEMORY_ACCESS :
+ * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable.
+ * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal.
+ * The below switch allow to select different access method for improved performance.
+ * Method 0 (default) : use `memcpy()`. Safe and portable.
+ * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable).
+ * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`.
+ * Method 2 : direct access. This method doesn't depend on compiler but violate C standard.
+ * It can generate buggy code on targets which do not support unaligned memory accesses.
+ * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6)
+ * See http://stackoverflow.com/a/32095106/646947 for details.
+ * Prefer these methods in priority order (0 > 1 > 2)
+ */
+#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */
+# if defined(__GNUC__) && defined(__ARM_FEATURE_UNALIGNED) && defined(__ARM_ARCH) && (__ARM_ARCH == 6)
+# define XXH_FORCE_MEMORY_ACCESS 2
+# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \
+ (defined(__GNUC__) && (defined(__ARM_ARCH) && __ARM_ARCH >= 7))
+# define XXH_FORCE_MEMORY_ACCESS 1
+# endif
+#endif
+
+/*!XXH_ACCEPT_NULL_INPUT_POINTER :
+ * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault.
+ * When this macro is enabled, xxHash actively checks input for null pointer.
+ * It it is, result for null input pointers is the same as a null-length input.
+ */
+#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */
+# define XXH_ACCEPT_NULL_INPUT_POINTER 0
+#endif
+
+/*!XXH_FORCE_ALIGN_CHECK :
+ * This is a minor performance trick, only useful with lots of very small keys.
+ * It means : check for aligned/unaligned input.
+ * The check costs one initial branch per hash;
+ * set it to 0 when the input is guaranteed to be aligned,
+ * or when alignment doesn't matter for performance.
+ */
+#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */
+# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+# define XXH_FORCE_ALIGN_CHECK 0
+# else
+# define XXH_FORCE_ALIGN_CHECK 1
+# endif
+#endif
+
+/*!XXH_REROLL:
+ * Whether to reroll XXH32_finalize, and XXH64_finalize,
+ * instead of using an unrolled jump table/if statement loop.
+ *
+ * This is automatically defined on -Os/-Oz on GCC and Clang. */
+#ifndef XXH_REROLL
+# if defined(__OPTIMIZE_SIZE__)
+# define XXH_REROLL 1
+# else
+# define XXH_REROLL 0
+# endif
+#endif
+
+/* *************************************
+* Includes & Memory related functions
+***************************************/
+/*! Modify the local functions below should you wish to use some other memory routines
+* for malloc(), free() */
+#include <stdlib.h>
+static void* XXH_malloc(size_t s) { return malloc(s); }
+static void XXH_free (void* p) { free(p); }
+/*! and for memcpy() */
+#include <string.h>
+static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); }
+
+#include <limits.h> /* ULLONG_MAX */
+
+#define XXH_STATIC_LINKING_ONLY
+#include "xxhash.h"
+
+
+/* *************************************
+* Compiler Specific Options
+***************************************/
+#ifdef _MSC_VER /* Visual Studio */
+# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */
+# define XXH_FORCE_INLINE static __forceinline
+# define XXH_NO_INLINE static __declspec(noinline)
+#else
+# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */
+# ifdef __GNUC__
+# define XXH_FORCE_INLINE static inline __attribute__((always_inline))
+# define XXH_NO_INLINE static __attribute__((noinline))
+# else
+# define XXH_FORCE_INLINE static inline
+# define XXH_NO_INLINE static
+# endif
+# else
+# define XXH_FORCE_INLINE static
+# define XXH_NO_INLINE static
+# endif /* __STDC_VERSION__ */
+#endif
+
+
+
+/* *************************************
+* Debug
+***************************************/
+/* DEBUGLEVEL is expected to be defined externally,
+ * typically through compiler command line.
+ * Value must be a number. */
+#ifndef DEBUGLEVEL
+# define DEBUGLEVEL 0
+#endif
+
+#if (DEBUGLEVEL>=1)
+# include <assert.h> /* note : can still be disabled with NDEBUG */
+# define XXH_ASSERT(c) assert(c)
+#else
+# define XXH_ASSERT(c) ((void)0)
+#endif
+
+/* note : use after variable declarations */
+#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; }
+
+
+/* *************************************
+* Basic Types
+***************************************/
+#ifndef MEM_MODULE
+# if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint8_t BYTE;
+ typedef uint16_t U16;
+ typedef uint32_t U32;
+# else
+ typedef unsigned char BYTE;
+ typedef unsigned short U16;
+ typedef unsigned int U32;
+# endif
+#endif
+
+
+/* === Memory access === */
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U32 u32; } __attribute__((packed)) unalign;
+static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; }
+
+#else
+
+/* portable and safe solution. Generally efficient.
+ * see : http://stackoverflow.com/a/32095106/646947
+ */
+static U32 XXH_read32(const void* memPtr)
+{
+ U32 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+
+/* === Endianess === */
+typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess;
+
+/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */
+#ifndef XXH_CPU_LITTLE_ENDIAN
+static int XXH_isLittleEndian(void)
+{
+ const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */
+ return one.c[0];
+}
+# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian()
+#endif
+
+
+
+
+/* ****************************************
+* Compiler-specific Functions and Macros
+******************************************/
+#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
+
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+#if !defined(NO_CLANG_BUILTIN) && __has_builtin(__builtin_rotateleft32) && __has_builtin(__builtin_rotateleft64)
+# define XXH_rotl32 __builtin_rotateleft32
+# define XXH_rotl64 __builtin_rotateleft64
+/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */
+#elif defined(_MSC_VER)
+# define XXH_rotl32(x,r) _rotl(x,r)
+# define XXH_rotl64(x,r) _rotl64(x,r)
+#else
+# define XXH_rotl32(x,r) (((x) << (r)) | ((x) >> (32 - (r))))
+# define XXH_rotl64(x,r) (((x) << (r)) | ((x) >> (64 - (r))))
+#endif
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap32 _byteswap_ulong
+#elif XXH_GCC_VERSION >= 403
+# define XXH_swap32 __builtin_bswap32
+#else
+static U32 XXH_swap32 (U32 x)
+{
+ return ((x << 24) & 0xff000000 ) |
+ ((x << 8) & 0x00ff0000 ) |
+ ((x >> 8) & 0x0000ff00 ) |
+ ((x >> 24) & 0x000000ff );
+}
+#endif
+
+
+/* ***************************
+* Memory reads
+*****************************/
+typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment;
+
+XXH_FORCE_INLINE U32 XXH_readLE32(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr));
+}
+
+static U32 XXH_readBE32(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr);
+}
+
+XXH_FORCE_INLINE U32
+XXH_readLE32_align(const void* ptr, XXH_alignment align)
+{
+ if (align==XXH_unaligned) {
+ return XXH_readLE32(ptr);
+ } else {
+ return XXH_CPU_LITTLE_ENDIAN ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr);
+ }
+}
+
+
+/* *************************************
+* Misc
+***************************************/
+XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; }
+
+
+/* *******************************************************************
+* 32-bit hash functions
+*********************************************************************/
+static const U32 PRIME32_1 = 0x9E3779B1U; /* 0b10011110001101110111100110110001 */
+static const U32 PRIME32_2 = 0x85EBCA77U; /* 0b10000101111010111100101001110111 */
+static const U32 PRIME32_3 = 0xC2B2AE3DU; /* 0b11000010101100101010111000111101 */
+static const U32 PRIME32_4 = 0x27D4EB2FU; /* 0b00100111110101001110101100101111 */
+static const U32 PRIME32_5 = 0x165667B1U; /* 0b00010110010101100110011110110001 */
+
+static U32 XXH32_round(U32 acc, U32 input)
+{
+ acc += input * PRIME32_2;
+ acc = XXH_rotl32(acc, 13);
+ acc *= PRIME32_1;
+#if defined(__GNUC__) && defined(__SSE4_1__) && !defined(XXH_ENABLE_AUTOVECTORIZE)
+ /* UGLY HACK:
+ * This inline assembly hack forces acc into a normal register. This is the
+ * only thing that prevents GCC and Clang from autovectorizing the XXH32 loop
+ * (pragmas and attributes don't work for some resason) without globally
+ * disabling SSE4.1.
+ *
+ * The reason we want to avoid vectorization is because despite working on
+ * 4 integers at a time, there are multiple factors slowing XXH32 down on
+ * SSE4:
+ * - There's a ridiculous amount of lag from pmulld (10 cycles of latency on newer chips!)
+ * making it slightly slower to multiply four integers at once compared to four
+ * integers independently. Even when pmulld was fastest, Sandy/Ivy Bridge, it is
+ * still not worth it to go into SSE just to multiply unless doing a long operation.
+ *
+ * - Four instructions are required to rotate,
+ * movqda tmp, v // not required with VEX encoding
+ * pslld tmp, 13 // tmp <<= 13
+ * psrld v, 19 // x >>= 19
+ * por v, tmp // x |= tmp
+ * compared to one for scalar:
+ * roll v, 13 // reliably fast across the board
+ * shldl v, v, 13 // Sandy Bridge and later prefer this for some reason
+ *
+ * - Instruction level parallelism is actually more beneficial here because the
+ * SIMD actually serializes this operation: While v1 is rotating, v2 can load data,
+ * while v3 can multiply. SSE forces them to operate together.
+ *
+ * How this hack works:
+ * __asm__("" // Declare an assembly block but don't declare any instructions
+ * : // However, as an Input/Output Operand,
+ * "+r" // constrain a read/write operand (+) as a general purpose register (r).
+ * (acc) // and set acc as the operand
+ * );
+ *
+ * Because of the 'r', the compiler has promised that seed will be in a
+ * general purpose register and the '+' says that it will be 'read/write',
+ * so it has to assume it has changed. It is like volatile without all the
+ * loads and stores.
+ *
+ * Since the argument has to be in a normal register (not an SSE register),
+ * each time XXH32_round is called, it is impossible to vectorize. */
+ __asm__("" : "+r" (acc));
+#endif
+ return acc;
+}
+
+/* mix all bits */
+static U32 XXH32_avalanche(U32 h32)
+{
+ h32 ^= h32 >> 15;
+ h32 *= PRIME32_2;
+ h32 ^= h32 >> 13;
+ h32 *= PRIME32_3;
+ h32 ^= h32 >> 16;
+ return(h32);
+}
+
+#define XXH_get32bits(p) XXH_readLE32_align(p, align)
+
+static U32
+XXH32_finalize(U32 h32, const void* ptr, size_t len, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)ptr;
+
+#define PROCESS1 \
+ h32 += (*p++) * PRIME32_5; \
+ h32 = XXH_rotl32(h32, 11) * PRIME32_1 ;
+
+#define PROCESS4 \
+ h32 += XXH_get32bits(p) * PRIME32_3; \
+ p+=4; \
+ h32 = XXH_rotl32(h32, 17) * PRIME32_4 ;
+
+ /* Compact rerolled version */
+ if (XXH_REROLL) {
+ len &= 15;
+ while (len >= 4) {
+ PROCESS4;
+ len -= 4;
+ }
+ while (len > 0) {
+ PROCESS1;
+ --len;
+ }
+ return XXH32_avalanche(h32);
+ } else {
+ switch(len&15) /* or switch(bEnd - p) */ {
+ case 12: PROCESS4;
+ /* fallthrough */
+ case 8: PROCESS4;
+ /* fallthrough */
+ case 4: PROCESS4;
+ return XXH32_avalanche(h32);
+
+ case 13: PROCESS4;
+ /* fallthrough */
+ case 9: PROCESS4;
+ /* fallthrough */
+ case 5: PROCESS4;
+ PROCESS1;
+ return XXH32_avalanche(h32);
+
+ case 14: PROCESS4;
+ /* fallthrough */
+ case 10: PROCESS4;
+ /* fallthrough */
+ case 6: PROCESS4;
+ PROCESS1;
+ PROCESS1;
+ return XXH32_avalanche(h32);
+
+ case 15: PROCESS4;
+ /* fallthrough */
+ case 11: PROCESS4;
+ /* fallthrough */
+ case 7: PROCESS4;
+ /* fallthrough */
+ case 3: PROCESS1;
+ /* fallthrough */
+ case 2: PROCESS1;
+ /* fallthrough */
+ case 1: PROCESS1;
+ /* fallthrough */
+ case 0: return XXH32_avalanche(h32);
+ }
+ XXH_ASSERT(0);
+ return h32; /* reaching this point is deemed impossible */
+ }
+}
+
+XXH_FORCE_INLINE U32
+XXH32_endian_align(const void* input, size_t len, U32 seed, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* bEnd = p + len;
+ U32 h32;
+
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ if (p==NULL) {
+ len=0;
+ bEnd=p=(const BYTE*)(size_t)16;
+ }
+#endif
+
+ if (len>=16) {
+ const BYTE* const limit = bEnd - 15;
+ U32 v1 = seed + PRIME32_1 + PRIME32_2;
+ U32 v2 = seed + PRIME32_2;
+ U32 v3 = seed + 0;
+ U32 v4 = seed - PRIME32_1;
+
+ do {
+ v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4;
+ v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4;
+ v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4;
+ v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4;
+ } while (p < limit);
+
+ h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7)
+ + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18);
+ } else {
+ h32 = seed + PRIME32_5;
+ }
+
+ h32 += (U32)len;
+
+ return XXH32_finalize(h32, p, len&15, align);
+}
+
+
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t len, unsigned int seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH32_state_t state;
+ XXH32_reset(&state, seed);
+ XXH32_update(&state, input, len);
+ return XXH32_digest(&state);
+
+#else
+
+ if (XXH_FORCE_ALIGN_CHECK) {
+ if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */
+ return XXH32_endian_align(input, len, seed, XXH_aligned);
+ } }
+
+ return XXH32_endian_align(input, len, seed, XXH_unaligned);
+#endif
+}
+
+
+
+/*====== Hash streaming ======*/
+
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void)
+{
+ return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState)
+{
+ memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed)
+{
+ XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
+ memset(&state, 0, sizeof(state));
+ state.v1 = seed + PRIME32_1 + PRIME32_2;
+ state.v2 = seed + PRIME32_2;
+ state.v3 = seed + 0;
+ state.v4 = seed - PRIME32_1;
+ /* do not write into reserved, planned to be removed in a future version */
+ memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved));
+ return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH_errorcode
+XXH32_update(XXH32_state_t* state, const void* input, size_t len)
+{
+ if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ return XXH_OK;
+#else
+ return XXH_ERROR;
+#endif
+
+ { const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+ state->total_len_32 += (XXH32_hash_t)len;
+ state->large_len |= (XXH32_hash_t)((len>=16) | (state->total_len_32>=16));
+
+ if (state->memsize + len < 16) { /* fill in tmp buffer */
+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len);
+ state->memsize += (XXH32_hash_t)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) { /* some data left from previous update */
+ XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize);
+ { const U32* p32 = state->mem32;
+ state->v1 = XXH32_round(state->v1, XXH_readLE32(p32)); p32++;
+ state->v2 = XXH32_round(state->v2, XXH_readLE32(p32)); p32++;
+ state->v3 = XXH32_round(state->v3, XXH_readLE32(p32)); p32++;
+ state->v4 = XXH32_round(state->v4, XXH_readLE32(p32));
+ }
+ p += 16-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p <= bEnd-16) {
+ const BYTE* const limit = bEnd - 16;
+ U32 v1 = state->v1;
+ U32 v2 = state->v2;
+ U32 v3 = state->v3;
+ U32 v4 = state->v4;
+
+ do {
+ v1 = XXH32_round(v1, XXH_readLE32(p)); p+=4;
+ v2 = XXH32_round(v2, XXH_readLE32(p)); p+=4;
+ v3 = XXH32_round(v3, XXH_readLE32(p)); p+=4;
+ v4 = XXH32_round(v4, XXH_readLE32(p)); p+=4;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd) {
+ XXH_memcpy(state->mem32, p, (size_t)(bEnd-p));
+ state->memsize = (unsigned)(bEnd-p);
+ }
+ }
+
+ return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* state)
+{
+ U32 h32;
+
+ if (state->large_len) {
+ h32 = XXH_rotl32(state->v1, 1)
+ + XXH_rotl32(state->v2, 7)
+ + XXH_rotl32(state->v3, 12)
+ + XXH_rotl32(state->v4, 18);
+ } else {
+ h32 = state->v3 /* == seed */ + PRIME32_5;
+ }
+
+ h32 += state->total_len_32;
+
+ return XXH32_finalize(h32, state->mem32, state->memsize, XXH_aligned);
+}
+
+
+/*====== Canonical representation ======*/
+
+/*! Default XXH result types are basic unsigned 32 and 64 bits.
+* The canonical representation follows human-readable write convention, aka big-endian (large digits first).
+* These functions allow transformation of hash result into and from its canonical format.
+* This way, hash values can be written into a file or buffer, remaining comparable across different systems.
+*/
+
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash);
+ memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src)
+{
+ return XXH_readBE32(src);
+}
+
+
+#ifndef XXH_NO_LONG_LONG
+
+/* *******************************************************************
+* 64-bit hash functions
+*********************************************************************/
+
+/*====== Memory access ======*/
+
+#ifndef MEM_MODULE
+# define MEM_MODULE
+# if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint64_t U64;
+# else
+ /* if compiler doesn't support unsigned long long, replace by another 64-bit type */
+ typedef unsigned long long U64;
+# endif
+#endif
+
+/*! XXH_REROLL_XXH64:
+ * Whether to reroll the XXH64_finalize() loop.
+ *
+ * Just like XXH32, we can unroll the XXH64_finalize() loop. This can be a performance gain
+ * on 64-bit hosts, as only one jump is required.
+ *
+ * However, on 32-bit hosts, because arithmetic needs to be done with two 32-bit registers,
+ * and 64-bit arithmetic needs to be simulated, it isn't beneficial to unroll. The code becomes
+ * ridiculously large (the largest function in the binary on i386!), and rerolling it saves
+ * anywhere from 3kB to 20kB. It is also slightly faster because it fits into cache better
+ * and is more likely to be inlined by the compiler.
+ *
+ * If XXH_REROLL is defined, this is ignored and the loop is always rerolled. */
+#ifndef XXH_REROLL_XXH64
+# if (defined(__ILP32__) || defined(_ILP32)) /* ILP32 is often defined on 32-bit GCC family */ \
+ || !(defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64) /* x86-64 */ \
+ || defined(_M_ARM64) || defined(__aarch64__) || defined(__arm64__) /* aarch64 */ \
+ || defined(__PPC64__) || defined(__PPC64LE__) || defined(__ppc64__) || defined(__powerpc64__) /* ppc64 */ \
+ || defined(__mips64__) || defined(__mips64)) /* mips64 */ \
+ || (!defined(SIZE_MAX) || SIZE_MAX < ULLONG_MAX) /* check limits */
+# define XXH_REROLL_XXH64 1
+# else
+# define XXH_REROLL_XXH64 0
+# endif
+#endif /* !defined(XXH_REROLL_XXH64) */
+
+#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2))
+
+/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */
+static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; }
+
+#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1))
+
+/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */
+/* currently only defined for gcc and icc */
+typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64;
+static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; }
+
+#else
+
+/* portable and safe solution. Generally efficient.
+ * see : http://stackoverflow.com/a/32095106/646947
+ */
+
+static U64 XXH_read64(const void* memPtr)
+{
+ U64 val;
+ memcpy(&val, memPtr, sizeof(val));
+ return val;
+}
+
+#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */
+
+#if defined(_MSC_VER) /* Visual Studio */
+# define XXH_swap64 _byteswap_uint64
+#elif XXH_GCC_VERSION >= 403
+# define XXH_swap64 __builtin_bswap64
+#else
+static U64 XXH_swap64 (U64 x)
+{
+ return ((x << 56) & 0xff00000000000000ULL) |
+ ((x << 40) & 0x00ff000000000000ULL) |
+ ((x << 24) & 0x0000ff0000000000ULL) |
+ ((x << 8) & 0x000000ff00000000ULL) |
+ ((x >> 8) & 0x00000000ff000000ULL) |
+ ((x >> 24) & 0x0000000000ff0000ULL) |
+ ((x >> 40) & 0x000000000000ff00ULL) |
+ ((x >> 56) & 0x00000000000000ffULL);
+}
+#endif
+
+XXH_FORCE_INLINE U64 XXH_readLE64(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr));
+}
+
+static U64 XXH_readBE64(const void* ptr)
+{
+ return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr);
+}
+
+XXH_FORCE_INLINE U64
+XXH_readLE64_align(const void* ptr, XXH_alignment align)
+{
+ if (align==XXH_unaligned)
+ return XXH_readLE64(ptr);
+ else
+ return XXH_CPU_LITTLE_ENDIAN ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr);
+}
+
+
+/*====== xxh64 ======*/
+
+static const U64 PRIME64_1 = 0x9E3779B185EBCA87ULL; /* 0b1001111000110111011110011011000110000101111010111100101010000111 */
+static const U64 PRIME64_2 = 0xC2B2AE3D27D4EB4FULL; /* 0b1100001010110010101011100011110100100111110101001110101101001111 */
+static const U64 PRIME64_3 = 0x165667B19E3779F9ULL; /* 0b0001011001010110011001111011000110011110001101110111100111111001 */
+static const U64 PRIME64_4 = 0x85EBCA77C2B2AE63ULL; /* 0b1000010111101011110010100111011111000010101100101010111001100011 */
+static const U64 PRIME64_5 = 0x27D4EB2F165667C5ULL; /* 0b0010011111010100111010110010111100010110010101100110011111000101 */
+
+static U64 XXH64_round(U64 acc, U64 input)
+{
+ acc += input * PRIME64_2;
+ acc = XXH_rotl64(acc, 31);
+ acc *= PRIME64_1;
+ return acc;
+}
+
+static U64 XXH64_mergeRound(U64 acc, U64 val)
+{
+ val = XXH64_round(0, val);
+ acc ^= val;
+ acc = acc * PRIME64_1 + PRIME64_4;
+ return acc;
+}
+
+static U64 XXH64_avalanche(U64 h64)
+{
+ h64 ^= h64 >> 33;
+ h64 *= PRIME64_2;
+ h64 ^= h64 >> 29;
+ h64 *= PRIME64_3;
+ h64 ^= h64 >> 32;
+ return h64;
+}
+
+
+#define XXH_get64bits(p) XXH_readLE64_align(p, align)
+
+static U64
+XXH64_finalize(U64 h64, const void* ptr, size_t len, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)ptr;
+
+#define PROCESS1_64 \
+ h64 ^= (*p++) * PRIME64_5; \
+ h64 = XXH_rotl64(h64, 11) * PRIME64_1;
+
+#define PROCESS4_64 \
+ h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \
+ p+=4; \
+ h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3;
+
+#define PROCESS8_64 { \
+ U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \
+ p+=8; \
+ h64 ^= k1; \
+ h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \
+}
+
+ /* Rerolled version for 32-bit targets is faster and much smaller. */
+ if (XXH_REROLL || XXH_REROLL_XXH64) {
+ len &= 31;
+ while (len >= 8) {
+ PROCESS8_64;
+ len -= 8;
+ }
+ if (len >= 4) {
+ PROCESS4_64;
+ len -= 4;
+ }
+ while (len > 0) {
+ PROCESS1_64;
+ --len;
+ }
+ return XXH64_avalanche(h64);
+ } else {
+ switch(len & 31) {
+ case 24: PROCESS8_64;
+ /* fallthrough */
+ case 16: PROCESS8_64;
+ /* fallthrough */
+ case 8: PROCESS8_64;
+ return XXH64_avalanche(h64);
+
+ case 28: PROCESS8_64;
+ /* fallthrough */
+ case 20: PROCESS8_64;
+ /* fallthrough */
+ case 12: PROCESS8_64;
+ /* fallthrough */
+ case 4: PROCESS4_64;
+ return XXH64_avalanche(h64);
+
+ case 25: PROCESS8_64;
+ /* fallthrough */
+ case 17: PROCESS8_64;
+ /* fallthrough */
+ case 9: PROCESS8_64;
+ PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 29: PROCESS8_64;
+ /* fallthrough */
+ case 21: PROCESS8_64;
+ /* fallthrough */
+ case 13: PROCESS8_64;
+ /* fallthrough */
+ case 5: PROCESS4_64;
+ PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 26: PROCESS8_64;
+ /* fallthrough */
+ case 18: PROCESS8_64;
+ /* fallthrough */
+ case 10: PROCESS8_64;
+ PROCESS1_64;
+ PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 30: PROCESS8_64;
+ /* fallthrough */
+ case 22: PROCESS8_64;
+ /* fallthrough */
+ case 14: PROCESS8_64;
+ /* fallthrough */
+ case 6: PROCESS4_64;
+ PROCESS1_64;
+ PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 27: PROCESS8_64;
+ /* fallthrough */
+ case 19: PROCESS8_64;
+ /* fallthrough */
+ case 11: PROCESS8_64;
+ PROCESS1_64;
+ PROCESS1_64;
+ PROCESS1_64;
+ return XXH64_avalanche(h64);
+
+ case 31: PROCESS8_64;
+ /* fallthrough */
+ case 23: PROCESS8_64;
+ /* fallthrough */
+ case 15: PROCESS8_64;
+ /* fallthrough */
+ case 7: PROCESS4_64;
+ /* fallthrough */
+ case 3: PROCESS1_64;
+ /* fallthrough */
+ case 2: PROCESS1_64;
+ /* fallthrough */
+ case 1: PROCESS1_64;
+ /* fallthrough */
+ case 0: return XXH64_avalanche(h64);
+ }
+ }
+ /* impossible to reach */
+ XXH_ASSERT(0);
+ return 0; /* unreachable, but some compilers complain without it */
+}
+
+XXH_FORCE_INLINE U64
+XXH64_endian_align(const void* input, size_t len, U64 seed, XXH_alignment align)
+{
+ const BYTE* p = (const BYTE*)input;
+ const BYTE* bEnd = p + len;
+ U64 h64;
+
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ if (p==NULL) {
+ len=0;
+ bEnd=p=(const BYTE*)(size_t)32;
+ }
+#endif
+
+ if (len>=32) {
+ const BYTE* const limit = bEnd - 32;
+ U64 v1 = seed + PRIME64_1 + PRIME64_2;
+ U64 v2 = seed + PRIME64_2;
+ U64 v3 = seed + 0;
+ U64 v4 = seed - PRIME64_1;
+
+ do {
+ v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8;
+ v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8;
+ v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8;
+ v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8;
+ } while (p<=limit);
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+ h64 = XXH64_mergeRound(h64, v1);
+ h64 = XXH64_mergeRound(h64, v2);
+ h64 = XXH64_mergeRound(h64, v3);
+ h64 = XXH64_mergeRound(h64, v4);
+
+ } else {
+ h64 = seed + PRIME64_5;
+ }
+
+ h64 += (U64) len;
+
+ return XXH64_finalize(h64, p, len, align);
+}
+
+
+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t len, unsigned long long seed)
+{
+#if 0
+ /* Simple version, good for code maintenance, but unfortunately slow for small inputs */
+ XXH64_state_t state;
+ XXH64_reset(&state, seed);
+ XXH64_update(&state, input, len);
+ return XXH64_digest(&state);
+
+#else
+
+ if (XXH_FORCE_ALIGN_CHECK) {
+ if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */
+ return XXH64_endian_align(input, len, seed, XXH_aligned);
+ } }
+
+ return XXH64_endian_align(input, len, seed, XXH_unaligned);
+
+#endif
+}
+
+/*====== Hash Streaming ======*/
+
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void)
+{
+ return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t));
+}
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr)
+{
+ XXH_free(statePtr);
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState)
+{
+ memcpy(dstState, srcState, sizeof(*dstState));
+}
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed)
+{
+ XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */
+ memset(&state, 0, sizeof(state));
+ state.v1 = seed + PRIME64_1 + PRIME64_2;
+ state.v2 = seed + PRIME64_2;
+ state.v3 = seed + 0;
+ state.v4 = seed - PRIME64_1;
+ /* do not write into reserved64, might be removed in a future version */
+ memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved64));
+ return XXH_OK;
+}
+
+XXH_PUBLIC_API XXH_errorcode
+XXH64_update (XXH64_state_t* state, const void* input, size_t len)
+{
+ if (input==NULL)
+#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1)
+ return XXH_OK;
+#else
+ return XXH_ERROR;
+#endif
+
+ { const BYTE* p = (const BYTE*)input;
+ const BYTE* const bEnd = p + len;
+
+ state->total_len += len;
+
+ if (state->memsize + len < 32) { /* fill in tmp buffer */
+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len);
+ state->memsize += (U32)len;
+ return XXH_OK;
+ }
+
+ if (state->memsize) { /* tmp buffer is full */
+ XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize);
+ state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0));
+ state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1));
+ state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2));
+ state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3));
+ p += 32-state->memsize;
+ state->memsize = 0;
+ }
+
+ if (p+32 <= bEnd) {
+ const BYTE* const limit = bEnd - 32;
+ U64 v1 = state->v1;
+ U64 v2 = state->v2;
+ U64 v3 = state->v3;
+ U64 v4 = state->v4;
+
+ do {
+ v1 = XXH64_round(v1, XXH_readLE64(p)); p+=8;
+ v2 = XXH64_round(v2, XXH_readLE64(p)); p+=8;
+ v3 = XXH64_round(v3, XXH_readLE64(p)); p+=8;
+ v4 = XXH64_round(v4, XXH_readLE64(p)); p+=8;
+ } while (p<=limit);
+
+ state->v1 = v1;
+ state->v2 = v2;
+ state->v3 = v3;
+ state->v4 = v4;
+ }
+
+ if (p < bEnd) {
+ XXH_memcpy(state->mem64, p, (size_t)(bEnd-p));
+ state->memsize = (unsigned)(bEnd-p);
+ }
+ }
+
+ return XXH_OK;
+}
+
+
+XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* state)
+{
+ U64 h64;
+
+ if (state->total_len >= 32) {
+ U64 const v1 = state->v1;
+ U64 const v2 = state->v2;
+ U64 const v3 = state->v3;
+ U64 const v4 = state->v4;
+
+ h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18);
+ h64 = XXH64_mergeRound(h64, v1);
+ h64 = XXH64_mergeRound(h64, v2);
+ h64 = XXH64_mergeRound(h64, v3);
+ h64 = XXH64_mergeRound(h64, v4);
+ } else {
+ h64 = state->v3 /*seed*/ + PRIME64_5;
+ }
+
+ h64 += (U64) state->total_len;
+
+ return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, XXH_aligned);
+}
+
+
+/*====== Canonical representation ======*/
+
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash)
+{
+ XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t));
+ if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash);
+ memcpy(dst, &hash, sizeof(*dst));
+}
+
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src)
+{
+ return XXH_readBE64(src);
+}
+
+
+
+/* *********************************************************************
+* XXH3
+* New generation hash designed for speed on small keys and vectorization
+************************************************************************ */
+
+#include "xxh3.h"
+
+
+#endif /* XXH_NO_LONG_LONG */
diff --git a/src/libs/decoders/xxhash.h b/src/libs/decoders/xxhash.h
new file mode 100644
index 000000000..d7fb88e30
--- /dev/null
+++ b/src/libs/decoders/xxhash.h
@@ -0,0 +1,548 @@
+/*
+ xxHash - Extremely Fast Hash algorithm
+ Header File
+ Copyright (C) 2012-2016, Yann Collet.
+
+ BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php)
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ You can contact the author at :
+ - xxHash source repository : https://github.com/Cyan4973/xxHash
+*/
+
+/* Notice extracted from xxHash homepage :
+
+xxHash is an extremely fast Hash algorithm, running at RAM speed limits.
+It also successfully passes all tests from the SMHasher suite.
+
+Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz)
+
+Name Speed Q.Score Author
+xxHash 5.4 GB/s 10
+CrapWow 3.2 GB/s 2 Andrew
+MumurHash 3a 2.7 GB/s 10 Austin Appleby
+SpookyHash 2.0 GB/s 10 Bob Jenkins
+SBox 1.4 GB/s 9 Bret Mulvey
+Lookup3 1.2 GB/s 9 Bob Jenkins
+SuperFastHash 1.2 GB/s 1 Paul Hsieh
+CityHash64 1.05 GB/s 10 Pike & Alakuijala
+FNV 0.55 GB/s 5 Fowler, Noll, Vo
+CRC32 0.43 GB/s † 9
+MD5-32 0.33 GB/s 10 Ronald L. Rivest
+SHA1-32 0.28 GB/s 10
+
+Note †: other CRC32 implementations can be over 40x faster than SMHasher's:
+http://fastcompression.blogspot.com/2019/03/presenting-xxh3.html?showComment=1552696407071#c3490092340461170735
+
+Q.Score is a measure of quality of the hash function.
+It depends on successfully passing SMHasher test set.
+10 is a perfect score.
+
+A 64-bit version, named XXH64, is available since r35.
+It offers much better speed, but for 64-bit applications only.
+Name Speed on 64 bits Speed on 32 bits
+XXH64 13.8 GB/s 1.9 GB/s
+XXH32 6.8 GB/s 6.0 GB/s
+*/
+
+#ifndef XXHASH_H_5627135585666179
+#define XXHASH_H_5627135585666179 1
+
+#if defined (__cplusplus)
+extern "C" {
+#endif
+
+
+/* ****************************
+* Definitions
+******************************/
+#include <stddef.h> /* size_t */
+typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode;
+
+
+/* ****************************
+ * API modifier
+ ******************************/
+/** XXH_INLINE_ALL (and XXH_PRIVATE_API)
+ * This build macro includes xxhash functions in `static` mode
+ * in order to inline them, and remove their symbol from the public list.
+ * Inlining offers great performance improvement on small keys,
+ * and dramatic ones when length is expressed as a compile-time constant.
+ * See https://fastcompression.blogspot.com/2018/03/xxhash-for-small-keys-impressive-power.html .
+ * Methodology :
+ * #define XXH_INLINE_ALL
+ * #include "xxhash.h"
+ * `xxhash.c` is automatically included.
+ * It's not useful to compile and link it as a separate object.
+ */
+#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)
+# ifndef XXH_STATIC_LINKING_ONLY
+# define XXH_STATIC_LINKING_ONLY
+# endif
+# if defined(__GNUC__)
+# define XXH_PUBLIC_API static __inline __attribute__((unused))
+# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */)
+# define XXH_PUBLIC_API static inline
+# elif defined(_MSC_VER)
+# define XXH_PUBLIC_API static __inline
+# else
+ /* this version may generate warnings for unused static functions */
+# define XXH_PUBLIC_API static
+# endif
+#else
+# if defined(WIN32) && defined(_MSC_VER) && (defined(XXH_IMPORT) || defined(XXH_EXPORT))
+# ifdef XXH_EXPORT
+# define XXH_PUBLIC_API __declspec(dllexport)
+# elif XXH_IMPORT
+# define XXH_PUBLIC_API __declspec(dllimport)
+# endif
+# else
+# define XXH_PUBLIC_API /* do nothing */
+# endif
+#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */
+
+/*! XXH_NAMESPACE, aka Namespace Emulation :
+ *
+ * If you want to include _and expose_ xxHash functions from within your own library,
+ * but also want to avoid symbol collisions with other libraries which may also include xxHash,
+ *
+ * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library
+ * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values).
+ *
+ * Note that no change is required within the calling program as long as it includes `xxhash.h` :
+ * regular symbol name will be automatically translated by this header.
+ */
+#ifdef XXH_NAMESPACE
+# define XXH_CAT(A,B) A##B
+# define XXH_NAME2(A,B) XXH_CAT(A,B)
+# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber)
+# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32)
+# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState)
+# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState)
+# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset)
+# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update)
+# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest)
+# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState)
+# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash)
+# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical)
+# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64)
+# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState)
+# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState)
+# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset)
+# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update)
+# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest)
+# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState)
+# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash)
+# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical)
+#endif
+
+
+/* *************************************
+* Version
+***************************************/
+#define XXH_VERSION_MAJOR 0
+#define XXH_VERSION_MINOR 7
+#define XXH_VERSION_RELEASE 1
+#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE)
+XXH_PUBLIC_API unsigned XXH_versionNumber (void);
+
+
+/*-**********************************************************************
+* 32-bit hash
+************************************************************************/
+#if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint32_t XXH32_hash_t;
+#else
+ typedef unsigned int XXH32_hash_t;
+#endif
+
+/*! XXH32() :
+ Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input".
+ The memory between input & input+length must be valid (allocated and read-accessible).
+ "seed" can be used to alter the result predictably.
+ Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */
+XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed);
+
+/*====== Streaming ======*/
+typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */
+XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr);
+XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state);
+
+XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed);
+XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr);
+
+/*
+ * Streaming functions generate the xxHash of an input provided in multiple segments.
+ * Note that, for small input, they are slower than single-call functions, due to state management.
+ * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized.
+ *
+ * XXH state must first be allocated, using XXH*_createState() .
+ *
+ * Start a new hash by initializing state with a seed, using XXH*_reset().
+ *
+ * Then, feed the hash state by calling XXH*_update() as many times as necessary.
+ * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error.
+ *
+ * Finally, a hash value can be produced anytime, by using XXH*_digest().
+ * This function returns the nn-bits hash as an int or long long.
+ *
+ * It's still possible to continue inserting input into the hash state after a digest,
+ * and generate some new hashes later on, by calling again XXH*_digest().
+ *
+ * When done, free XXH state space if it was allocated dynamically.
+ */
+
+/*====== Canonical representation ======*/
+
+typedef struct { unsigned char digest[4]; } XXH32_canonical_t;
+XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash);
+XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src);
+
+/* Default result type for XXH functions are primitive unsigned 32 and 64 bits.
+ * The canonical representation uses human-readable write convention, aka big-endian (large digits first).
+ * These functions allow transformation of hash result into and from its canonical format.
+ * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs.
+ */
+
+
+#ifndef XXH_NO_LONG_LONG
+/*-**********************************************************************
+* 64-bit hash
+************************************************************************/
+#if !defined (__VMS) \
+ && (defined (__cplusplus) \
+ || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) )
+# include <stdint.h>
+ typedef uint64_t XXH64_hash_t;
+#else
+ typedef unsigned long long XXH64_hash_t;
+#endif
+
+/*! XXH64() :
+ Calculate the 64-bit hash of sequence of length "len" stored at memory address "input".
+ "seed" can be used to alter the result predictably.
+ This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark).
+*/
+XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed);
+
+/*====== Streaming ======*/
+typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */
+XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr);
+XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state);
+
+XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed);
+XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr);
+
+/*====== Canonical representation ======*/
+typedef struct { unsigned char digest[8]; } XXH64_canonical_t;
+XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash);
+XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src);
+
+
+#endif /* XXH_NO_LONG_LONG */
+
+
+
+#ifdef XXH_STATIC_LINKING_ONLY
+
+/* ================================================================================================
+ This section contains declarations which are not guaranteed to remain stable.
+ They may change in future versions, becoming incompatible with a different version of the library.
+ These declarations should only be used with static linking.
+ Never use them in association with dynamic linking !
+=================================================================================================== */
+
+/* These definitions are only present to allow
+ * static allocation of XXH state, on stack or in a struct for example.
+ * Never **ever** use members directly. */
+
+struct XXH32_state_s {
+ XXH32_hash_t total_len_32;
+ XXH32_hash_t large_len;
+ XXH32_hash_t v1;
+ XXH32_hash_t v2;
+ XXH32_hash_t v3;
+ XXH32_hash_t v4;
+ XXH32_hash_t mem32[4];
+ XXH32_hash_t memsize;
+ XXH32_hash_t reserved; /* never read nor write, might be removed in a future version */
+}; /* typedef'd to XXH32_state_t */
+
+#ifndef XXH_NO_LONG_LONG /* remove 64-bit support */
+struct XXH64_state_s {
+ XXH64_hash_t total_len;
+ XXH64_hash_t v1;
+ XXH64_hash_t v2;
+ XXH64_hash_t v3;
+ XXH64_hash_t v4;
+ XXH64_hash_t mem64[4];
+ XXH32_hash_t memsize;
+ XXH32_hash_t reserved32; /* required for padding anyway */
+ XXH64_hash_t reserved64; /* never read nor write, might be removed in a future version */
+}; /* typedef'd to XXH64_state_t */
+#endif /* XXH_NO_LONG_LONG */
+
+
+/*-**********************************************************************
+* XXH3
+* New experimental hash
+************************************************************************/
+#ifndef XXH_NO_LONG_LONG
+
+
+/* ============================================
+ * XXH3 is a new hash algorithm,
+ * featuring improved speed performance for both small and large inputs.
+ * See full speed analysis at : http://fastcompression.blogspot.com/2019/03/presenting-xxh3.html
+ * In general, expect XXH3 to run about ~2x faster on large inputs,
+ * and >3x faster on small ones, though exact differences depend on platform.
+ *
+ * The algorithm is portable, will generate the same hash on all platforms.
+ * It benefits greatly from vectorization units, but does not require it.
+ *
+ * XXH3 offers 2 variants, _64bits and _128bits.
+ * When only 64 bits are needed, prefer calling the _64bits variant :
+ * it reduces the amount of mixing, resulting in faster speed on small inputs.
+ * It's also generally simpler to manipulate a scalar return type than a struct.
+ *
+ * The XXH3 algorithm is still considered experimental.
+ * Produced results can still change between versions.
+ * For example, results produced by v0.7.1 are not comparable with results from v0.7.0 .
+ * It's nonetheless possible to use XXH3 for ephemeral data (local sessions),
+ * but avoid storing values in long-term storage for later re-use.
+ *
+ * The API supports one-shot hashing, streaming mode, and custom secrets.
+ *
+ * There are still a number of opened questions that community can influence during the experimental period.
+ * I'm trying to list a few of them below, though don't consider this list as complete.
+ *
+ * - 128-bits output type : currently defined as a structure of two 64-bits fields.
+ * That's because 128-bit values do not exist in C standard.
+ * Note that it means that, at byte level, result is not identical depending on endianess.
+ * However, at field level, they are identical on all platforms.
+ * The canonical representation solves the issue of identical byte-level representation across platforms,
+ * which is necessary for serialization.
+ * Would there be a better representation for a 128-bit hash result ?
+ * Are the names of the inner 64-bit fields important ? Should they be changed ?
+ *
+ * - Seed type for 128-bits variant : currently, it's a single 64-bit value, like the 64-bit variant.
+ * It could be argued that it's more logical to offer a 128-bit seed input parameter for a 128-bit hash.
+ * But 128-bit seed is more difficult to use, since it requires to pass a structure instead of a scalar value.
+ * Such a variant could either replace current one, or become an additional one.
+ * Farmhash, for example, offers both variants (the 128-bits seed variant is called `doubleSeed`).
+ * If both 64-bit and 128-bit seeds are possible, which variant should be called XXH128 ?
+ *
+ * - Result for len==0 : Currently, the result of hashing a zero-length input is `0`.
+ * It seems okay as a return value when using all "default" secret and seed (it used to be a request for XXH32/XXH64).
+ * But is it still fine to return `0` when secret or seed are non-default ?
+ * Are there use cases which could depend on generating a different hash result for zero-length input when the secret is different ?
+ */
+
+#ifdef XXH_NAMESPACE
+# define XXH3_64bits XXH_NAME2(XXH_NAMESPACE, XXH3_64bits)
+# define XXH3_64bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSecret)
+# define XXH3_64bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_withSeed)
+
+# define XXH3_createState XXH_NAME2(XXH_NAMESPACE, XXH3_createState)
+# define XXH3_freeState XXH_NAME2(XXH_NAMESPACE, XXH3_freeState)
+# define XXH3_copyState XXH_NAME2(XXH_NAMESPACE, XXH3_copyState)
+
+# define XXH3_64bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset)
+# define XXH3_64bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSeed)
+# define XXH3_64bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_reset_withSecret)
+# define XXH3_64bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_update)
+# define XXH3_64bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_64bits_digest)
+#endif
+
+/* XXH3_64bits() :
+ * default 64-bit variant, using default secret and default seed of 0.
+ * It's the fastest variant. */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits(const void* data, size_t len);
+
+/* XXH3_64bits_withSecret() :
+ * It's possible to provide any blob of bytes as a "secret" to generate the hash.
+ * This makes it more difficult for an external actor to prepare an intentional collision.
+ * The secret *must* be large enough (>= XXH3_SECRET_SIZE_MIN).
+ * It should consist of random bytes.
+ * Avoid repeating same character, or sequences of bytes,
+ * and especially avoid swathes of \0.
+ * Failure to respect these conditions will result in a poor quality hash.
+ */
+#define XXH3_SECRET_SIZE_MIN 136
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize);
+
+/* XXH3_64bits_withSeed() :
+ * This variant generates on the fly a custom secret,
+ * based on the default secret, altered using the `seed` value.
+ * While this operation is decently fast, note that it's not completely free.
+ * note : seed==0 produces same results as XXH3_64bits() */
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_withSeed(const void* data, size_t len, XXH64_hash_t seed);
+
+
+/* streaming 64-bit */
+
+#if defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) /* C11+ */
+# include <stdalign.h>
+# define XXH_ALIGN(n) alignas(n)
+#elif defined(__GNUC__)
+# define XXH_ALIGN(n) __attribute__ ((aligned(n)))
+#elif defined(_MSC_VER)
+# define XXH_ALIGN(n) __declspec(align(n))
+#else
+# define XXH_ALIGN(n) /* disabled */
+#endif
+
+typedef struct XXH3_state_s XXH3_state_t;
+
+#define XXH3_SECRET_DEFAULT_SIZE 192 /* minimum XXH3_SECRET_SIZE_MIN */
+#define XXH3_INTERNALBUFFER_SIZE 256
+struct XXH3_state_s {
+ XXH_ALIGN(64) XXH64_hash_t acc[8];
+ XXH_ALIGN(64) char customSecret[XXH3_SECRET_DEFAULT_SIZE]; /* used to store a custom secret generated from the seed. Makes state larger. Design might change */
+ XXH_ALIGN(64) char buffer[XXH3_INTERNALBUFFER_SIZE];
+ XXH32_hash_t bufferedSize;
+ XXH32_hash_t nbStripesPerBlock;
+ XXH32_hash_t nbStripesSoFar;
+ XXH32_hash_t secretLimit;
+ XXH32_hash_t reserved32;
+ XXH32_hash_t reserved32_2;
+ XXH64_hash_t totalLen;
+ XXH64_hash_t seed;
+ XXH64_hash_t reserved64;
+ const void* secret; /* note : there is some padding after, due to alignment on 64 bytes */
+}; /* typedef'd to XXH3_state_t */
+
+/* Streaming requires state maintenance.
+ * This operation costs memory and cpu.
+ * As a consequence, streaming is slower than one-shot hashing.
+ * For better performance, prefer using one-shot functions whenever possible. */
+
+XXH_PUBLIC_API XXH3_state_t* XXH3_createState(void);
+XXH_PUBLIC_API XXH_errorcode XXH3_freeState(XXH3_state_t* statePtr);
+XXH_PUBLIC_API void XXH3_copyState(XXH3_state_t* dst_state, const XXH3_state_t* src_state);
+
+
+/* XXH3_64bits_reset() :
+ * initialize with default parameters.
+ * result will be equivalent to `XXH3_64bits()`. */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset(XXH3_state_t* statePtr);
+/* XXH3_64bits_reset_withSeed() :
+ * generate a custom secret from `seed`, and store it into state.
+ * digest will be equivalent to `XXH3_64bits_withSeed()`. */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed);
+/* XXH3_64bits_reset_withSecret() :
+ * `secret` is referenced, and must outlive the hash streaming session.
+ * secretSize must be >= XXH3_SECRET_SIZE_MIN.
+ */
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize);
+
+XXH_PUBLIC_API XXH_errorcode XXH3_64bits_update (XXH3_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH64_hash_t XXH3_64bits_digest (const XXH3_state_t* statePtr);
+
+
+/* 128-bit */
+
+#ifdef XXH_NAMESPACE
+# define XXH128 XXH_NAME2(XXH_NAMESPACE, XXH128)
+# define XXH3_128bits XXH_NAME2(XXH_NAMESPACE, XXH3_128bits)
+# define XXH3_128bits_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSeed)
+# define XXH3_128bits_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_withSecret)
+
+# define XXH3_128bits_reset XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset)
+# define XXH3_128bits_reset_withSeed XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSeed)
+# define XXH3_128bits_reset_withSecret XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_reset_withSecret)
+# define XXH3_128bits_update XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_update)
+# define XXH3_128bits_digest XXH_NAME2(XXH_NAMESPACE, XXH3_128bits_digest)
+
+# define XXH128_isEqual XXH_NAME2(XXH_NAMESPACE, XXH128_isEqual)
+# define XXH128_cmp XXH_NAME2(XXH_NAMESPACE, XXH128_cmp)
+# define XXH128_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH128_canonicalFromHash)
+# define XXH128_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH128_hashFromCanonical)
+#endif
+
+typedef struct {
+ XXH64_hash_t low64;
+ XXH64_hash_t high64;
+} XXH128_hash_t;
+
+XXH_PUBLIC_API XXH128_hash_t XXH128(const void* data, size_t len, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits(const void* data, size_t len);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSeed(const void* data, size_t len, XXH64_hash_t seed); /* == XXH128() */
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_withSecret(const void* data, size_t len, const void* secret, size_t secretSize);
+
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset(XXH3_state_t* statePtr);
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSeed(XXH3_state_t* statePtr, XXH64_hash_t seed);
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_reset_withSecret(XXH3_state_t* statePtr, const void* secret, size_t secretSize);
+
+XXH_PUBLIC_API XXH_errorcode XXH3_128bits_update (XXH3_state_t* statePtr, const void* input, size_t length);
+XXH_PUBLIC_API XXH128_hash_t XXH3_128bits_digest (const XXH3_state_t* statePtr);
+
+
+/* Note : for better performance, following functions should be inlined,
+ * using XXH_INLINE_ALL */
+
+/* return : 1 is equal, 0 if different */
+XXH_PUBLIC_API int XXH128_isEqual(XXH128_hash_t h1, XXH128_hash_t h2);
+
+/* This comparator is compatible with stdlib's qsort().
+ * return : >0 if *h128_1 > *h128_2
+ * <0 if *h128_1 < *h128_2
+ * =0 if *h128_1 == *h128_2 */
+XXH_PUBLIC_API int XXH128_cmp(const void* h128_1, const void* h128_2);
+
+
+/*====== Canonical representation ======*/
+typedef struct { unsigned char digest[16]; } XXH128_canonical_t;
+XXH_PUBLIC_API void XXH128_canonicalFromHash(XXH128_canonical_t* dst, XXH128_hash_t hash);
+XXH_PUBLIC_API XXH128_hash_t XXH128_hashFromCanonical(const XXH128_canonical_t* src);
+
+
+#endif /* XXH_NO_LONG_LONG */
+
+
+/*-**********************************************************************
+* XXH_INLINE_ALL
+************************************************************************/
+#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API)
+# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */
+#endif
+
+
+
+#endif /* XXH_STATIC_LINKING_ONLY */
+
+
+#if defined (__cplusplus)
+}
+#endif
+
+#endif /* XXHASH_H_5627135585666179 */