diff --git a/CMakeLists.txt b/CMakeLists.txt

index 15ff9a0a..263942e3 100644

--- a/CMakeLists.txt

+++ b/CMakeLists.txt

@@ -106,8 +106,11 @@ set (SOURCES

 src/visited.c

 src/visited.h

 # Audio playback:

+ src/audio/buf.c

+ src/audio/buf.h

 src/audio/player.c

 src/audio/player.h

+ src/audio/stb_vorbis.c

 # User interface:

 src/ui/color.c

 src/ui/color.h

diff --git a/src/audio/buf.c b/src/audio/buf.c

new file mode 100644

index 00000000..e61164d4

--- /dev/null

+++ b/src/audio/buf.c

@@ -0,0 +1,107 @@

+/* Copyright 2020 Jaakko Keränen jaakko.keranen@iki.fi

+

+Redistribution and use in source and binary forms, with or without

+modification, are permitted provided that the following conditions are met:

+

+1. Redistributions of source code must retain the above copyright notice, this

+ list of conditions and the following disclaimer.

+2. 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. */

+

+#include "buf.h"

+

+iDefineTypeConstruction(InputBuf)

+

+void init_InputBuf(iInputBuf *d) {

+ init_Mutex(&d->mtx);

+ init_Condition(&d->changed);

+ init_Block(&d->data, 0);

+ d->isComplete = iTrue;

+}

+

+void deinit_InputBuf(iInputBuf *d) {

+ deinit_Block(&d->data);

+ deinit_Condition(&d->changed);

+ deinit_Mutex(&d->mtx);

+}

+

+size_t size_InputBuf(const iInputBuf *d) {

+ return size_Block(&d->data);

+}

+

+/----------------------------------------------------------------------------------------------/

+

+iDefineTypeConstructionArgs(SampleBuf, (SDL_AudioFormat format, size_t numChannels, size_t count),

+ format, numChannels, count)

+

+void init_SampleBuf(iSampleBuf *d, SDL_AudioFormat format, size_t numChannels, size_t count) {

+ d->format = format;

+ d->numChannels = numChannels;

+ d->sampleSize = SDL_AUDIO_BITSIZE(format) / 8 * numChannels;

+ d->count = count + 1; /* considered empty if head==tail */

+ d->data = malloc(d->sampleSize * d->count);

+ d->head = 0;

+ d->tail = 0;

+ init_Condition(&d->moreNeeded);

+}

+

+void deinit_SampleBuf(iSampleBuf *d) {

+ deinit_Condition(&d->moreNeeded);

+ free(d->data);

+}

+

+size_t size_SampleBuf(const iSampleBuf *d) {

+ return d->head - d->tail;

+}

+

+size_t vacancy_SampleBuf(const iSampleBuf *d) {

+ return d->count - size_SampleBuf(d) - 1;

+}

+

+iBool isFull_SampleBuf(const iSampleBuf *d) {

+ return vacancy_SampleBuf(d) == 0;

+}

+

+void write_SampleBuf(iSampleBuf *d, const void *samples, const size_t n) {

+ iAssert(n <= vacancy_SampleBuf(d));

+ const size_t headPos = d->head % d->count;

+ const size_t avail = d->count - headPos;

+ if (n > avail) {

+ const char *in = samples;

+ memcpy(ptr_SampleBuf_(d, headPos), in, d->sampleSize * avail);

+ in += d->sampleSize * avail;

+ memcpy(ptr_SampleBuf_(d, 0), in, d->sampleSize * (n - avail));

+ }

+ else {

+ memcpy(ptr_SampleBuf_(d, headPos), samples, d->sampleSize * n);

+ }

+ d->head += n;

+}

+

+void read_SampleBuf(iSampleBuf *d, const size_t n, void *samples_out) {

+ iAssert(n <= size_SampleBuf(d));

+ const size_t tailPos = d->tail % d->count;

+ const size_t avail = d->count - tailPos;

+ if (n > avail) {

+ char *out = samples_out;

+ memcpy(out, ptr_SampleBuf_(d, tailPos), d->sampleSize * avail);

+ out += d->sampleSize * avail;

+ memcpy(out, ptr_SampleBuf_(d, 0), d->sampleSize * (n - avail));

+ }

+ else {

+ memcpy(samples_out, ptr_SampleBuf_(d, tailPos), d->sampleSize * n);

+ }

+ d->tail += n;

+}

diff --git a/src/audio/buf.h b/src/audio/buf.h

new file mode 100644

index 00000000..de123481

--- /dev/null

+++ b/src/audio/buf.h

@@ -0,0 +1,74 @@

+/* Copyright 2020 Jaakko Keränen jaakko.keranen@iki.fi

+

+Redistribution and use in source and binary forms, with or without

+modification, are permitted provided that the following conditions are met:

+

+1. Redistributions of source code must retain the above copyright notice, this

+ list of conditions and the following disclaimer.

+2. 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. */

+

+#pragma once

+

+#include "the_Foundation/block.h"

+#include "the_Foundation/mutex.h"

+

+#include <SDL_audio.h>

+

+iDeclareType(InputBuf)

+iDeclareType(SampleBuf)

+

+#if !defined (AUDIO_S24LSB)

+# define AUDIO_S24LSB 0x8018 /* 24-bit integer samples */

+#endif

+#if !defined (AUDIO_F64LSB)

+# define AUDIO_F64LSB 0x8140 /* 64-bit floating point samples */

+#endif

+

+struct Impl_InputBuf {

+ iMutex mtx;

+ iCondition changed;

+ iBlock data;

+ iBool isComplete;

+};

+

+iDeclareTypeConstruction(InputBuf)

+

+size_t size_InputBuf (const iInputBuf *);

+

+/----------------------------------------------------------------------------------------------/

+

+struct Impl_SampleBuf {

+ SDL_AudioFormat format;

+ uint8_t numChannels;

+ uint8_t sampleSize; /* as bytes; one sample includes values for all channels */

+ void * data;

+ size_t count;

+ size_t head, tail;

+ iCondition moreNeeded;

+};

+

+iDeclareTypeConstructionArgs(SampleBuf, SDL_AudioFormat format, size_t numChannels, size_t count)

+

+size_t size_SampleBuf (const iSampleBuf *);

+iBool isFull_SampleBuf (const iSampleBuf *);

+size_t vacancy_SampleBuf (const iSampleBuf *);

+

+iLocalDef void *ptr_SampleBuf_(iSampleBuf *d, size_t pos) {

+ return ((char *) d->data) + (d->sampleSize * pos);

+}

+

+void write_SampleBuf (iSampleBuf *, const void *samples, const size_t n);

+void read_SampleBuf (iSampleBuf *, const size_t n, void *samples_out);

diff --git a/src/audio/player.c b/src/audio/player.c

index 07f41f01..0825dabd 100644

--- a/src/audio/player.c

+++ b/src/audio/player.c

@@ -21,146 +21,38 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */



#include "player.h"

+#include "buf.h"

+

+#define STB_VORBIS_HEADER_ONLY

+#include "stb_vorbis.c"



#include <the_Foundation/buffer.h>

#include <the_Foundation/thread.h>

#include <SDL_audio.h>



-iDeclareType(InputBuf)

-

-#if !defined (AUDIO_S24LSB)

-# define AUDIO_S24LSB 0x8018 /* 24-bit integer samples */

-#endif

-#if !defined (AUDIO_F64LSB)

-# define AUDIO_F64LSB 0x8140 /* 64-bit floating point samples */

-#endif

-

-struct Impl_InputBuf {

- iMutex mtx;

- iCondition changed;

- iBlock data;

- iBool isComplete;

-};

-

-void init_InputBuf(iInputBuf *d) {

- init_Mutex(&d->mtx);

- init_Condition(&d->changed);

- init_Block(&d->data, 0);

- d->isComplete = iTrue;

-}

-

-void deinit_InputBuf(iInputBuf *d) {

- deinit_Block(&d->data);

- deinit_Condition(&d->changed);

- deinit_Mutex(&d->mtx);

-}

-

-size_t size_InputBuf(const iInputBuf *d) {

- return size_Block(&d->data);

-}

-

-iDefineTypeConstruction(InputBuf)

-

-/----------------------------------------------------------------------------------------------/

-

-iDeclareType(SampleBuf)

-

-struct Impl_SampleBuf {

- SDL_AudioFormat format;

- uint8_t numChannels;

- uint8_t sampleSize; /* as bytes; one sample includes values for all channels */

- void * data;

- size_t count;

- size_t head, tail;

- iCondition moreNeeded;

-};

-

-void init_SampleBuf(iSampleBuf *d, SDL_AudioFormat format, size_t numChannels, size_t count) {

- d->format = format;

- d->numChannels = numChannels;

- d->sampleSize = SDL_AUDIO_BITSIZE(format) / 8 * numChannels;

- d->count = count + 1; /* considered empty if head==tail */

- d->data = malloc(d->sampleSize * d->count);

- d->head = 0;

- d->tail = 0;

- init_Condition(&d->moreNeeded);

-}

-

-void deinit_SampleBuf(iSampleBuf *d) {

- deinit_Condition(&d->moreNeeded);

- free(d->data);

-}

-

-size_t size_SampleBuf(const iSampleBuf *d) {

- return d->head - d->tail;

-}

-

-size_t vacancy_SampleBuf(const iSampleBuf *d) {

- return d->count - size_SampleBuf(d) - 1;

-}

-

-iBool isFull_SampleBuf(const iSampleBuf *d) {

- return vacancy_SampleBuf(d) == 0;

-}

-

-iLocalDef void *ptr_SampleBuf_(iSampleBuf *d, size_t pos) {

- return ((char *) d->data) + (d->sampleSize * pos);

-}

-

-void write_SampleBuf(iSampleBuf *d, const void *samples, const size_t n) {

- iAssert(n <= vacancy_SampleBuf(d));

- const size_t headPos = d->head % d->count;

- const size_t avail = d->count - headPos;

- if (n > avail) {

- const char *in = samples;

- memcpy(ptr_SampleBuf_(d, headPos), in, d->sampleSize * avail);

- in += d->sampleSize * avail;

- memcpy(ptr_SampleBuf_(d, 0), in, d->sampleSize * (n - avail));

- }

- else {

- memcpy(ptr_SampleBuf_(d, headPos), samples, d->sampleSize * n);

- }

- d->head += n;

-}

-

-void read_SampleBuf(iSampleBuf *d, const size_t n, void *samples_out) {

- iAssert(n <= size_SampleBuf(d));

- const size_t tailPos = d->tail % d->count;

- const size_t avail = d->count - tailPos;

- if (n > avail) {

- char *out = samples_out;

- memcpy(out, ptr_SampleBuf_(d, tailPos), d->sampleSize * avail);

- out += d->sampleSize * avail;

- memcpy(out, ptr_SampleBuf_(d, 0), d->sampleSize * (n - avail));

- }

- else {

- memcpy(samples_out, ptr_SampleBuf_(d, tailPos), d->sampleSize * n);

- }

- d->tail += n;

-}

-

/----------------------------------------------------------------------------------------------/



iDeclareType(ContentSpec)



-struct Impl_ContentSpec {

- SDL_AudioFormat inputFormat;

- SDL_AudioSpec output;

- size_t totalInputSize;

- uint64_t totalSamples;

- iRanges wavData;

-};

-

-iDeclareType(Decoder)

-

enum iDecoderType {

 none_DecoderType,

 wav_DecoderType,

- mpeg_DecoderType,

 vorbis_DecoderType,

+ mpeg_DecoderType,

 midi_DecoderType,

};



+struct Impl_ContentSpec {

+ enum iDecoderType type;

+ SDL_AudioFormat inputFormat;

+ SDL_AudioSpec output;

+ size_t totalInputSize;

+ uint64_t totalSamples;

+ iRanges dataRange;

+};

+

+iDeclareType(Decoder)

+

struct Impl_Decoder {

 enum iDecoderType type;

 float             gain;

@@ -176,12 +68,12 @@ struct Impl_Decoder {

 iRanges           wavData;

};



-enum iDecoderParseStatus {

- ok_DecoderParseStatus,

- needMoreInput_DecoderParseStatus,

+enum iDecoderStatus {

+ ok_DecoderStatus,

+ needMoreInput_DecoderStatus,

};



-static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRange) {

+static enum iDecoderStatus decodeWav_Decoder_(iDecoder *d, iRanges inputRange) {

 const uint8_t numChannels     = d->output.numChannels;

 const size_t  inputSampleSize = numChannels * SDL_AUDIO_BITSIZE(d->inputFormat) / 8;

 const size_t  vacancy         = vacancy_SampleBuf(&d->output);

@@ -189,11 +81,11 @@ static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRang

 const size_t  avail =

     iMin(inputRange.end - inputBytePos, d->wavData.end - inputBytePos) / inputSampleSize;

 if (avail == 0) {

- return needMoreInput_DecoderParseStatus;

+ return needMoreInput_DecoderStatus;

 }

 const size_t n = iMin(vacancy, avail);

 if (n == 0) {

- return ok_DecoderParseStatus;

+ return ok_DecoderStatus;

 }

 void *samples = malloc(inputSampleSize * n);

 /* Get a copy of the input for mixing. */ {

@@ -259,7 +151,7 @@ static enum iDecoderParseStatus parseWav_Decoder_(iDecoder *d, iRanges inputRang

 iGuardMutex(&d->outputMutex, write_SampleBuf(&d->output, samples, n));

 d->currentSample += n;

 free(samples);

- return ok_DecoderParseStatus;

+ return ok_DecoderStatus;

}



static iThreadResult run_Decoder_(iThread *thread) {

@@ -273,17 +165,17 @@ static iThreadResult run_Decoder_(iThread *thread) {

     iAssert(inputRange.start <= inputRange.end);

     if (!d->type) break;

     /* Have data to work on and a place to save output? */

- enum iDecoderParseStatus status = ok_DecoderParseStatus;

+ enum iDecoderStatus status = ok_DecoderStatus;

     if (!isEmpty_Range(&inputRange)) {

         switch (d->type) {

             case wav_DecoderType:

- status = parseWav_Decoder_(d, inputRange);

+ status = decodeWav_Decoder_(d, inputRange);

                 break;

             default:

                 break;

         }

     }

- if (status == needMoreInput_DecoderParseStatus) {

+ if (status == needMoreInput_DecoderStatus) {

         lock_Mutex(&d->input->mtx);

         if (size_InputBuf(d->input) == inputSize) {

             wait_Condition(&d->input->changed, &d->input->mtx);

@@ -302,10 +194,10 @@ static iThreadResult run_Decoder_(iThread *thread) {

}



void init_Decoder(iDecoder *d, iInputBuf *input, const iContentSpec *spec) {

- d->type = wav_DecoderType;

+ d->type = spec->type;

 d->gain        = 0.5f;

 d->input       = input;

- d->inputPos = spec->wavData.start;

+ d->inputPos = spec->dataRange.start;

 d->inputFormat = spec->inputFormat;

 d->totalInputSize = spec->totalInputSize;

 init_SampleBuf(&d->output,

@@ -330,16 +222,22 @@ void deinit_Decoder(iDecoder *d) {

 deinit_SampleBuf(&d->output);

}



+static void start_Decoder_(iDecoder *d) {

+ if (!d->thread && d->type != none_DecoderType) {

+ }

+}

+

iDefineTypeConstructionArgs(Decoder, (iInputBuf *input, const iContentSpec *spec),

                         input, spec)



/----------------------------------------------------------------------------------------------/



struct Impl_Player {

- SDL_AudioSpec spec;

+ SDL_AudioSpec spec;

 SDL_AudioDeviceID device;

- iInputBuf *data;

- iDecoder *decoder;

+ iString mime;

+ iInputBuf * data;

+ iDecoder * decoder;

};



iDefineTypeConstruction(Player)

@@ -358,8 +256,8 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {

 const size_t dataSize = size_InputBuf(d->data);

 iBuffer *buf = iClob(new_Buffer());

 open_Buffer(buf, &d->data->data);

- enum iDecoderType decType = wav_DecoderType; /* TODO: from MIME */

- if (decType == wav_DecoderType && dataSize >= 44) {

+ content.type = wav_DecoderType; /* TODO: from MIME */

+ if (content.type == wav_DecoderType && dataSize >= 44) {

     /* Read the RIFF/WAVE header. */

     iStream *is = stream_Buffer(buf);

     char magic[4];

@@ -428,8 +326,8 @@ static iContentSpec contentSpec_Player_(const iPlayer *d) {

             }

         }

         else if (memcmp(magic, "data", 4) == 0) {

- content.wavData = (iRanges){ pos_Stream(is), pos_Stream(is) + size };

- content.totalSamples = (uint64_t) size_Range(&content.wavData) / blockAlign;

+ content.dataRange = (iRanges){ pos_Stream(is), pos_Stream(is) + size };

+ content.totalSamples = (uint64_t) size_Range(&content.dataRange) / blockAlign;

             break;

         }

         else {

@@ -462,6 +360,7 @@ static void writeOutputSamples_Player_(void *plr, Uint8 *stream, int len) {



void init_Player(iPlayer *d) {

 iZap(d->spec);

+ init_String(&d->mime);

 d->device  = 0;

 d->decoder = NULL;

 d->data    = new_InputBuf();

@@ -470,6 +369,7 @@ void init_Player(iPlayer *d) {

void deinit_Player(iPlayer *d) {

 stop_Player(d);

 delete_InputBuf(d->data);

+ deinit_String(&d->mime);

}



iBool isStarted_Player(const iPlayer *d) {

@@ -481,13 +381,14 @@ iBool isPaused_Player(const iPlayer *d) {

 return SDL_GetAudioDeviceStatus(d->device) == SDL_AUDIO_PAUSED;

}



-void setFormatHint_Player(iPlayer *d, const char *hint) {

-}

-

-void updateSourceData_Player(iPlayer *d, const iBlock *data, enum iPlayerUpdate update) {

+void updateSourceData_Player(iPlayer *d, const iString *mimeType, const iBlock *data,

+ enum iPlayerUpdate update) {

 /* TODO: Add MIME as argument */

 iInputBuf *input = d->data;

 lock_Mutex(&input->mtx);

+ if (mimeType) {

+ set_String(&d->mime, mimeType);

+ }

 switch (update) {

     case replace_PlayerUpdate:

         set_Block(&input->data, data);

@@ -515,7 +416,7 @@ iBool start_Player(iPlayer *d) {

 if (isStarted_Player(d)) {

     return iFalse;

 }

- iContentSpec content = contentSpec_Player_(d);

+ iContentSpec content = contentSpec_Player_(d);

 content.output.callback = writeOutputSamples_Player_;

 content.output.userdata = d;

 d->device = SDL_OpenAudioDevice(NULL, SDL_FALSE /* playback */, &content.output, &d->spec, 0);

diff --git a/src/audio/player.h b/src/audio/player.h

index fe6717b0..720f2d78 100644

--- a/src/audio/player.h

+++ b/src/audio/player.h

@@ -33,8 +33,8 @@ enum iPlayerUpdate {

 complete_PlayerUpdate,

};



-void setFormatHint_Player (iPlayer *, const char *hint);

-void updateSourceData_Player (iPlayer *, const iBlock *data, enum iPlayerUpdate update);

+void updateSourceData_Player (iPlayer *, const iString *mimeType, const iBlock *data,

+ enum iPlayerUpdate update);



iBool start_Player (iPlayer *);

void setPaused_Player (iPlayer *, iBool isPaused);

diff --git a/src/audio/stb_vorbis.c b/src/audio/stb_vorbis.c

new file mode 100644

index 00000000..a8cbfa6c

--- /dev/null

+++ b/src/audio/stb_vorbis.c

@@ -0,0 +1,5563 @@

+// Ogg Vorbis audio decoder - v1.20 - 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 github:alxprd

+// Tom Beaumont Ingo Leitgeb Nicolas Guillemot

+// Phillip Bennefall Rohit Thiago Goulart

+// github:manxorist saga musix github:infatum

+// Timur Gagiev Maxwell Koo Peter Waller

+// github:audinowho Dougall Johnson David Reid

+// github:Clownacy Pedro J. Estebanez Remi Verschelde

+//

+// Partial history:

+// 1.20 - 2020-07-11 - several small fixes

+// 1.19 - 2020-02-05 - warnings

+// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.

+// 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

+

+/////////// 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;

+

+typedef struct

+{

+ char *vendor;

+

+ int comment_list_length;

+ char **comment_list;

+} stb_vorbis_comment;

+

+// get general information about the file

+extern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);

+

+// get ogg comments

+extern stb_vorbis_comment stb_vorbis_get_comment(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

+

+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) || defined(NEWLIB)

+ #include <alloca.h>

+ #endif

+#else // STB_VORBIS_NO_CRT

+ #define NULL 0

+ #define malloc(s) 0

+ #define free(s) ((void) 0)

+ #define realloc(s) 0

+#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

+ #ifndef alloca

+ #define alloca __builtin_alloca

+ #endif

+#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)

+

+

+typedef unsigned char uint8;

+typedef signed char int8;

+typedef unsigned short uint16;

+typedef signed short int16;

+typedef unsigned int uint32;

+typedef signed int int32;

+

+#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;

+

+ char *vendor;

+ int comment_list_length;

+ char **comment_list;

+

+ // input config

+#ifndef STB_VORBIS_NO_STDIO

+ FILE *f;

+ uint32 f_start;

+ int close_on_free;

+#endif

+

+ uint8 *stream;

+ uint8 *stream_start;

+ uint8 *stream_end;

+

+ uint32 stream_len;

+

+ uint8 push_mode;

+

+ // the page to seek to when seeking to start, may be zero

+ uint32 first_audio_page_offset;

+

+ // p_first is the page on which the first audio packet ends

+ // (but not necessarily the page on which it starts)

+ 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) (void)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+7) & ~7; // round up to nearest 8 for alignment of future allocs.

+ 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+7) & ~7; // round up to nearest 8 for alignment of future allocs.

+ 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+7)&~7;

+ 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(4kM_PI/n);

+ A[k2+1] = (float) -sin(4kM_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 //////////////////////////

+

+

+#if 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++;

+ }

+

+ #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;

+ }

+

+ #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;

+ }

+ #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;

+ }

+ }

+ #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;

+ if (f->first_decode && !IS_PUSH_MODE(f)) {

+ f->p_first.page_start = stb_vorbis_get_file_offset(f) - 4;

+ }

+ // 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;

+ len = 0;

+ for (i=0; i < f->segment_count; ++i)

+ len += f->segments[i];

+ len += 27 + f->segment_count;

+ f->p_first.page_end = f->p_first.page_start + len;

+ f->p_first.last_decoded_sample = loc0;

+ }

+ 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-

Proxy Information
Original URL
gemini://git.skyjake.fi/lagrange/release/pcdiff/4bf163ecfac27c3dd86dff96df6f4647f9afe021
Status Code
Success (20)
Meta
text/plain
Capsule Response Time
36.955177 milliseconds
Gemini-to-HTML Time
25.92169 milliseconds

This content has been proxied by September (ba2dc).