From 4facc03a64bece341c553a53c92c19a27137c1ab Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 25 Jul 2016 08:50:40 +0200 Subject: Upload ptheads Win32 DLL --- src/external/pthread/lib/pthreadGC2.dll | Bin 0 -> 119888 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/external/pthread/lib/pthreadGC2.dll (limited to 'src/external') diff --git a/src/external/pthread/lib/pthreadGC2.dll b/src/external/pthread/lib/pthreadGC2.dll new file mode 100644 index 00000000..67b9289d Binary files /dev/null and b/src/external/pthread/lib/pthreadGC2.dll differ -- cgit v1.2.3 From a008d49230c0f7543d8921e68f757dea7934a8c5 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Fri, 29 Jul 2016 13:17:50 +0200 Subject: Corrected some issues to compile with MSC --- src/audio.c | 4 ++++ src/external/jar_xm.h | 2 +- src/rlgl.c | 47 ++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 43 insertions(+), 10 deletions(-) (limited to 'src/external') diff --git a/src/audio.c b/src/audio.c index 3c618dd2..3e2d8347 100644 --- a/src/audio.c +++ b/src/audio.c @@ -78,6 +78,10 @@ #define JAR_MOD_IMPLEMENTATION #include "external/jar_mod.h" // MOD loading functions +#ifdef _MSC_VER + #undef bool +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- diff --git a/src/external/jar_xm.h b/src/external/jar_xm.h index f9ddb511..02463e08 100644 --- a/src/external/jar_xm.h +++ b/src/external/jar_xm.h @@ -1435,7 +1435,7 @@ static void jar_xm_volume_slide(jar_xm_channel_context_t* ch, uint8_t rawval) { } } -static float jar_xm_envelope_lerp(jar_xm_envelope_point_t* restrict a, jar_xm_envelope_point_t* restrict b, uint16_t pos) { +static float jar_xm_envelope_lerp(jar_xm_envelope_point_t* a, jar_xm_envelope_point_t* b, uint16_t pos) { /* Linear interpolation between two envelope points */ if(pos <= a->frame) return a->value; else if(pos >= b->frame) return b->value; diff --git a/src/rlgl.c b/src/rlgl.c index 2b551469..d027495f 100644 --- a/src/rlgl.c +++ b/src/rlgl.c @@ -415,14 +415,14 @@ void rlMatrixMode(int mode) } } -void rlFrustum(double left, double right, double bottom, double top, double near, double far) +void rlFrustum(double left, double right, double bottom, double top, double zNear, double zFar) { - glFrustum(left, right, bottom, top, near, far); + glFrustum(left, right, bottom, top, zNear, zFar); } -void rlOrtho(double left, double right, double bottom, double top, double near, double far) +void rlOrtho(double left, double right, double bottom, double top, double zNear, double zFar) { - glOrtho(left, right, bottom, top, near, far); + glOrtho(left, right, bottom, top, zNear, zFar); } void rlPushMatrix(void) { glPushMatrix(); } @@ -1056,8 +1056,13 @@ void rlglInit(int width, int height) // We get a list of available extensions and we check for some of them (compressed textures) // NOTE: We don't need to check again supported extensions but we do (GLAD already dealt with that) glGetIntegerv(GL_NUM_EXTENSIONS, &numExt); - const char *extList[numExt]; +#ifdef _MSC_VER + const char **extList = malloc(sizeof(const char *)*numExt); +#else + const char *extList[numExt]; +#endif + for (int i = 0; i < numExt; i++) extList[i] = (char *)glGetStringi(GL_EXTENSIONS, i); #elif defined(GRAPHICS_API_OPENGL_ES2) @@ -1137,6 +1142,10 @@ void rlglInit(int width, int height) if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true; } +#ifdef _MSC_VER + free(extList); +#endif + #if defined(GRAPHICS_API_OPENGL_ES2) if (vaoSupported) TraceLog(INFO, "[EXTENSION] VAO extension detected, VAO functions initialized successfully"); else TraceLog(WARNING, "[EXTENSION] VAO extension not found, VAO usage not supported"); @@ -2891,11 +2900,18 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &maxLength); +#ifdef _MSC_VER + char *log = malloc(maxLength); +#else char log[maxLength]; - +#endif glGetShaderInfoLog(vertexShader, maxLength, &length, log); TraceLog(INFO, "%s", log); + +#ifdef _MSC_VER + free(log); +#endif } else TraceLog(INFO, "[VSHDR ID %i] Vertex shader compiled successfully", vertexShader); @@ -2912,11 +2928,18 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &maxLength); +#ifdef _MSC_VER + char *log = malloc(maxLength); +#else char log[maxLength]; - +#endif glGetShaderInfoLog(fragmentShader, maxLength, &length, log); TraceLog(INFO, "%s", log); + +#ifdef _MSC_VER + free(log); +#endif } else TraceLog(INFO, "[FSHDR ID %i] Fragment shader compiled successfully", fragmentShader); @@ -2950,12 +2973,18 @@ static unsigned int LoadShaderProgram(const char *vShaderStr, const char *fShade glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength); +#ifdef _MSC_VER + char *log = malloc(maxLength); +#else char log[maxLength]; - +#endif glGetProgramInfoLog(program, maxLength, &length, log); TraceLog(INFO, "%s", log); - + +#ifdef _MSC_VER + free(log); +#endif glDeleteProgram(program); program = 0; -- cgit v1.2.3 From 02c456432d7f284c41519f6d540ad6c4dfb4a065 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 1 Aug 2016 12:49:17 +0200 Subject: Complete review of audio system Still some work left... --- examples/audio_module_playing.c | 10 +- examples/audio_music_stream.c | 20 +- src/audio.c | 1091 ++++++++++++++------------------------- src/audio.h | 36 +- src/external/jar_xm.h | 60 ++- src/raylib.h | 32 +- 6 files changed, 464 insertions(+), 785 deletions(-) (limited to 'src/external') diff --git a/examples/audio_module_playing.c b/examples/audio_module_playing.c index 6189b866..07165c76 100644 --- a/examples/audio_module_playing.c +++ b/examples/audio_module_playing.c @@ -57,7 +57,9 @@ int main() // Create a RenderTexture2D to be used for render to texture RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight); - PlayMusicStream(0, "resources/audio/2t2m_spa.xm"); // Play module stream + Music xm = LoadMusicStream("resources/audio/2t2m_spa.xm"); + + PlayMusicStream(xm); float timePlayed = 0.0f; @@ -88,9 +90,9 @@ int main() } // Get timePlayed scaled to bar dimensions - timePlayed = (GetMusicTimePlayed(0)/GetMusicTimeLength(0)*(screenWidth - 40))*2; + timePlayed = (GetMusicTimePlayed(xm)/GetMusicTimeLength(xm)*(screenWidth - 40))*2; - UpdateMusicStream(0); // Update music buffer with new stream data + UpdateMusicStream(xm); // Update music buffer with new stream data //---------------------------------------------------------------------------------- // Draw @@ -129,6 +131,8 @@ int main() UnloadShader(shader); // Unload shader UnloadRenderTexture(target); // Unload render texture + UnloadMusicStream(xm); // Unload music stream buffers from RAM + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) CloseWindow(); // Close window and OpenGL context diff --git a/examples/audio_music_stream.c b/examples/audio_music_stream.c index e135a6e4..b96b85f7 100644 --- a/examples/audio_music_stream.c +++ b/examples/audio_music_stream.c @@ -24,7 +24,9 @@ int main() InitAudioDevice(); // Initialize audio device - PlayMusicStream(0, "resources/audio/guitar_noodling.ogg"); // Play music stream + Music music = LoadMusicStream("resources/audio/guitar_noodling.ogg"); + + PlayMusicStream(music); int framesCounter = 0; float timePlayed = 0.0f; @@ -58,12 +60,12 @@ int main() SetMusicVolume(volume); } */ - if (IsWindowMinimized()) PauseMusicStream(0); - else ResumeMusicStream(0); + if (IsWindowMinimized()) PauseMusicStream(music); + else ResumeMusicStream(music); - timePlayed = GetMusicTimePlayed(0)/GetMusicTimeLength(0)*100*4; // We scale by 4 to fit 400 pixels - - UpdateMusicStream(0); // Update music buffer with new stream data + timePlayed = GetMusicTimePlayed(music)/GetMusicTimeLength(music)*100*4; // We scale by 4 to fit 400 pixels + + UpdateMusicStream(music); // Update music buffer with new stream data //---------------------------------------------------------------------------------- // Draw @@ -83,9 +85,11 @@ int main() // De-Initialization //-------------------------------------------------------------------------------------- - CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) + UnloadMusicStream(music); // Unload music stream buffers from RAM + + CloseAudioDevice(); // Close audio device (music streaming is automatically stopped) - CloseWindow(); // Close window and OpenGL context + CloseWindow(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- return 0; diff --git a/src/audio.c b/src/audio.c index 93987c6e..94bbb376 100644 --- a/src/audio.c +++ b/src/audio.c @@ -55,6 +55,7 @@ #include // Required for: strcmp(), strncmp() #include // Required for: FILE, fopen(), fclose(), fread() +// Tokens defined by OpenAL extension: AL_EXT_float32 #ifndef AL_FORMAT_MONO_FLOAT32 #define AL_FORMAT_MONO_FLOAT32 0x10010 #endif @@ -85,55 +86,47 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define MAX_STREAM_BUFFERS 2 // Number of buffers for each source -#define MAX_MUSIC_STREAMS 2 // Number of simultanious music sources -#define MAX_MIX_CHANNELS 4 // Number of mix channels (OpenAL sources) - -#if defined(PLATFORM_RPI) || defined(PLATFORM_ANDROID) - // NOTE: On RPI and Android should be lower to avoid frame-stalls - #define MUSIC_BUFFER_SIZE_SHORT 4096*2 // PCM data buffer (short) - 16Kb (RPI) - #define MUSIC_BUFFER_SIZE_FLOAT 4096 // PCM data buffer (float) - 16Kb (RPI) -#else - // NOTE: On HTML5 (emscripten) this is allocated on heap, by default it's only 16MB!...just take care... - #define MUSIC_BUFFER_SIZE_SHORT 4096*8 // PCM data buffer (short) - 64Kb - #define MUSIC_BUFFER_SIZE_FLOAT 4096*4 // PCM data buffer (float) - 64Kb -#endif +#define MAX_STREAM_BUFFERS 2 // Number of buffers for each audio stream + +// NOTE: Music buffer size is defined by number of samples, independent of sample size +// After some math, considering a sampleRate of 48000, a buffer refill rate of 1/60 seconds +// and double-buffering system, I concluded that a 4096 samples buffer should be enough +// In case of music-stalls, just inclease this number +#define AUDIO_BUFFER_SIZE 4096 // PCM data samples (i.e. short: 32Kb) //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- +typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_MODULE_XM, MUSIC_MODULE_MOD } MusicContextType; + // Used to create custom audio streams that are not bound to a specific file. -// There can be no more than 4 concurrent mixchannels in use. -// This is due to each active mixc being tied to a dedicated mix channel. -typedef struct MixChannel { - unsigned short sampleRate; // default is 48000 - unsigned char channels; // 1=mono,2=stereo - unsigned char mixChannel; // 0-3 or mixA-mixD, each mix channel can receive up to one dedicated audio stream - bool floatingPoint; // if false then the short datatype is used instead - bool playing; // false if paused - - ALenum alFormat; // OpenAL format specifier - ALuint alSource; // OpenAL source - ALuint alBuffer[MAX_STREAM_BUFFERS]; // OpenAL sample buffer -} MixChannel; +typedef struct AudioStream { + unsigned int sampleRate; // Frequency (samples per second): default is 48000 + unsigned int sampleSize; // BitDepth (bits per sample): 8, 16, 32 (24 not supported) + unsigned int channels; // Number of channels + + ALenum format; // OpenAL format specifier + ALuint source; // OpenAL source + ALuint buffers[MAX_STREAM_BUFFERS]; // OpenAL buffers (double buffering) +} AudioStream; // Music type (file streaming from memory) -// NOTE: Anything longer than ~10 seconds should be streamed into a mix channel... typedef struct Music { - stb_vorbis *stream; - jar_xm_context_t *xmctx; // XM chiptune context - jar_mod_context_t modctx; // MOD chiptune context - MixChannel *mixc; // Mix channel - - unsigned int totalSamplesLeft; - float totalLengthSeconds; - bool loop; - bool chipTune; // chiptune is loaded? - bool enabled; -} Music; - -// Audio errors registered + MusicContextType ctxType; // Type of music context (OGG, XM, MOD) + stb_vorbis *ctxOgg; // OGG audio context + jar_xm_context_t *ctxXm; // XM chiptune context + jar_mod_context_t ctxMod; // MOD chiptune context + + AudioStream stream; // Audio stream + + bool loop; // Repeat music after finish (loop) + unsigned int totalSamples; // Total number of samples + unsigned int samplesLeft; // Number of samples left to end +} MusicData, *Music; + +// Audio errors to register +/* typedef enum { ERROR_RAW_CONTEXT_CREATION = 1, ERROR_XM_CONTEXT_CREATION = 2, @@ -152,6 +145,7 @@ typedef enum { ERROR_UNINITIALIZED_CHANNELS = 16384, ERROR_UNINTIALIZED_MUSIC_BUFFER = 32768 } AudioError; +*/ #if defined(AUDIO_STANDALONE) typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; @@ -160,10 +154,7 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static Music musicStreams[MAX_MUSIC_STREAMS]; // Current music loaded, up to two can play at the same time -static MixChannel *mixChannels[MAX_MIX_CHANNELS]; // Mix channels currently active (from music streams) - -static int lastAudioError = 0; // Registers last audio error +static int lastAudioError = 0; // Registers last audio error //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -172,14 +163,11 @@ static Wave LoadWAV(const char *fileName); // Load WAV file static Wave LoadOGG(char *fileName); // Load OGG file static void UnloadWave(Wave wave); // Unload wave data -static bool BufferMusicStream(int index, int numBuffers); // Fill music buffers with data -static void EmptyMusicStream(int index); // Empty music buffers +static AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels); +static void CloseAudioStream(AudioStream stream); // Frees mix channel +static int BufferAudioStream(AudioStream stream, void *data, int numberElements); // Pushes more audio data into mix channel -static MixChannel *InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint); -static void CloseMixChannel(MixChannel *mixc); // Frees mix channel -static int BufferMixChannel(MixChannel *mixc, void *data, int numberElements); // Pushes more audio data into mix channel -//static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len); // Pass two arrays of the same legnth in -//static void ResampleByteToFloat(char *chars, float *floats, unsigned short len); // Pass two arrays of same length in +static bool BufferMusicStream(Music music, int numBuffersToProcess); // Fill music buffers with data #if defined(AUDIO_STANDALONE) const char *GetExtension(const char *fileName); // Get the extension for a filename @@ -190,45 +178,44 @@ void TraceLog(int msgType, const char *text, ...); // Outputs a trace log messa // Module Functions Definition - Audio Device initialization and Closing //---------------------------------------------------------------------------------- -// Initialize audio device and mixc +// Initialize audio device void InitAudioDevice(void) { // Open and initialize a device with default settings ALCdevice *device = alcOpenDevice(NULL); if (!device) TraceLog(ERROR, "Audio device could not be opened"); - - ALCcontext *context = alcCreateContext(device, NULL); - - if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE)) + else { - if (context != NULL) alcDestroyContext(context); + ALCcontext *context = alcCreateContext(device, NULL); - alcCloseDevice(device); + if ((context == NULL) || (alcMakeContextCurrent(context) == ALC_FALSE)) + { + if (context != NULL) alcDestroyContext(context); - TraceLog(ERROR, "Could not setup mix channel"); - } + alcCloseDevice(device); - TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); + TraceLog(ERROR, "Could not initialize audio context"); + } + else + { + TraceLog(INFO, "Audio device and context initialized successfully: %s", alcGetString(device, ALC_DEVICE_SPECIFIER)); - // Listener definition (just for 2D) - alListener3f(AL_POSITION, 0, 0, 0); - alListener3f(AL_VELOCITY, 0, 0, 0); - alListener3f(AL_ORIENTATION, 0, 0, -1); + // Listener definition (just for 2D) + alListener3f(AL_POSITION, 0, 0, 0); + alListener3f(AL_VELOCITY, 0, 0, 0); + alListener3f(AL_ORIENTATION, 0, 0, -1); + } + } } // Close the audio device for all contexts void CloseAudioDevice(void) { - for (int index = 0; index < MAX_MUSIC_STREAMS; index++) - { - if (musicStreams[index].mixc) StopMusicStreamEx(index); // Stop music streaming and close current stream - } - ALCdevice *device; ALCcontext *context = alcGetCurrentContext(); - if (context == NULL) TraceLog(WARNING, "Could not get current mix channel for closing"); + if (context == NULL) TraceLog(WARNING, "Could not get current audio context for closing"); device = alcGetContextsDevice(context); @@ -252,300 +239,30 @@ bool IsAudioDeviceReady(void) } } -//---------------------------------------------------------------------------------- -// Module Functions Definition - Custom audio output -//---------------------------------------------------------------------------------- - -// Init mix channel for streaming -// The mixChannel is what audio muxing channel you want to operate on, 0-3 are the ones available. -// Each mix channel can only be used one at a time. -static MixChannel *InitMixChannel(unsigned short sampleRate, unsigned char mixChannel, unsigned char channels, bool floatingPoint) -{ - if (mixChannel >= MAX_MIX_CHANNELS) return NULL; - if (!IsAudioDeviceReady()) InitAudioDevice(); - - if (!mixChannels[mixChannel]) - { - MixChannel *mixc = (MixChannel *)malloc(sizeof(MixChannel)); - mixc->sampleRate = sampleRate; - mixc->channels = channels; - mixc->mixChannel = mixChannel; - mixc->floatingPoint = floatingPoint; - mixChannels[mixChannel] = mixc; - - // Setup OpenAL format - if (channels == 1) - { - if (floatingPoint) mixc->alFormat = AL_FORMAT_MONO_FLOAT32; - else mixc->alFormat = AL_FORMAT_MONO16; - } - else if (channels == 2) - { - if (floatingPoint) mixc->alFormat = AL_FORMAT_STEREO_FLOAT32; - else mixc->alFormat = AL_FORMAT_STEREO16; - } - - // Create an audio source - alGenSources(1, &mixc->alSource); - alSourcef(mixc->alSource, AL_PITCH, 1); - alSourcef(mixc->alSource, AL_GAIN, 1); - alSource3f(mixc->alSource, AL_POSITION, 0, 0, 0); - alSource3f(mixc->alSource, AL_VELOCITY, 0, 0, 0); - - // Create Buffer - alGenBuffers(MAX_STREAM_BUFFERS, mixc->alBuffer); - - // Fill buffers - for (int i = 0; i < MAX_STREAM_BUFFERS; i++) - { - // Initialize buffer with zeros by default - if (mixc->floatingPoint) - { - float pcm[MUSIC_BUFFER_SIZE_FLOAT] = { 0.0f }; - alBufferData(mixc->alBuffer[i], mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_FLOAT*sizeof(float), mixc->sampleRate); - } - else - { - short pcm[MUSIC_BUFFER_SIZE_SHORT] = { 0 }; - alBufferData(mixc->alBuffer[i], mixc->alFormat, pcm, MUSIC_BUFFER_SIZE_SHORT*sizeof(short), mixc->sampleRate); - } - } - - alSourceQueueBuffers(mixc->alSource, MAX_STREAM_BUFFERS, mixc->alBuffer); - mixc->playing = true; - alSourcePlay(mixc->alSource); - - return mixc; - } - - return NULL; -} - -// Frees buffer in mix channel -static void CloseMixChannel(MixChannel *mixc) -{ - if (mixc) - { - alSourceStop(mixc->alSource); - mixc->playing = false; - - // Flush out all queued buffers - ALuint buffer = 0; - int queued = 0; - alGetSourcei(mixc->alSource, AL_BUFFERS_QUEUED, &queued); - - while (queued > 0) - { - alSourceUnqueueBuffers(mixc->alSource, 1, &buffer); - queued--; - } - - // Delete source and buffers - alDeleteSources(1, &mixc->alSource); - alDeleteBuffers(MAX_STREAM_BUFFERS, mixc->alBuffer); - mixChannels[mixc->mixChannel] = NULL; - free(mixc); - mixc = NULL; - } -} - -// Pushes more audio data into mix channel, only one buffer per call -// Call "BufferMixChannel(mixc, NULL, 0)" if you want to pause the audio. -// Returns number of samples that where processed. -static int BufferMixChannel(MixChannel *mixc, void *data, int numberElements) -{ - if (!mixc || (mixChannels[mixc->mixChannel] != mixc)) return 0; // When there is two channels there must be an even number of samples - - if (!data || !numberElements) - { - // Pauses audio until data is given - if (mixc->playing) - { - alSourcePause(mixc->alSource); - mixc->playing = false; - } - - return 0; - } - else if (!mixc->playing) - { - // Restart audio otherwise - alSourcePlay(mixc->alSource); - mixc->playing = true; - } - - ALuint buffer = 0; - - alSourceUnqueueBuffers(mixc->alSource, 1, &buffer); - if (!buffer) return 0; - - if (mixc->floatingPoint) - { - // Process float buffers - float *ptr = (float *)data; - alBufferData(buffer, mixc->alFormat, ptr, numberElements*sizeof(float), mixc->sampleRate); - } - else - { - // Process short buffers - short *ptr = (short *)data; - alBufferData(buffer, mixc->alFormat, ptr, numberElements*sizeof(short), mixc->sampleRate); - } - - alSourceQueueBuffers(mixc->alSource, 1, &buffer); - - return numberElements; -} - -/* -// Convert data from short to float -// example usage: -// short sh[3] = {1,2,3};float fl[3]; -// ResampleShortToFloat(sh,fl,3); -static void ResampleShortToFloat(short *shorts, float *floats, unsigned short len) -{ - for (int i = 0; i < len; i++) - { - if (shorts[i] < 0) floats[i] = (float)shorts[i]/32766.0f; - else floats[i] = (float)shorts[i]/32767.0f; - } -} - -// Convert data from float to short -// example usage: -// char ch[3] = {1,2,3};float fl[3]; -// ResampleByteToFloat(ch,fl,3); -static void ResampleByteToFloat(char *chars, float *floats, unsigned short len) -{ - for (int i = 0; i < len; i++) - { - if (chars[i] < 0) floats[i] = (float)chars[i]/127.0f; - else floats[i] = (float)chars[i]/128.0f; - } -} -*/ - -// Initialize raw audio mix channel for audio buffering -// NOTE: Returns mix channel index or -1 if it fails (errors are registered on lastAudioError) -int InitRawMixChannel(int sampleRate, int channels, bool floatingPoint) -{ - int mixIndex; - - for (mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot - { - if (mixChannels[mixIndex] == NULL) break; - else if (mixIndex == (MAX_MIX_CHANNELS - 1)) - { - lastAudioError = ERROR_OUT_OF_MIX_CHANNELS; - return -1; - } - } - - if (InitMixChannel(sampleRate, mixIndex, channels, floatingPoint)) return mixIndex; - else - { - lastAudioError = ERROR_RAW_CONTEXT_CREATION; - return -1; - } -} - -// Buffers data directly to raw mix channel -// if 0 is returned, buffers are still full and you need to keep trying with the same data -// otherwise it will return number of samples buffered. -// NOTE: Data could be either be an array of floats or shorts, depending on the created context -int BufferRawAudioContext(int ctx, void *data, unsigned short numberElements) -{ - int numBuffered = 0; - - if (ctx >= 0) - { - MixChannel *mixc = mixChannels[ctx]; - numBuffered = BufferMixChannel(mixc, data, numberElements); - } - - return numBuffered; -} - -// Closes and frees raw mix channel -void CloseRawAudioContext(int ctx) -{ - if (mixChannels[ctx]) CloseMixChannel(mixChannels[ctx]); -} - //---------------------------------------------------------------------------------- // Module Functions Definition - Sounds loading and playing (.WAV) //---------------------------------------------------------------------------------- // Load sound to memory +// NOTE: The entire file is loaded to memory to be played (no-streaming) Sound LoadSound(char *fileName) { - Sound sound = { 0 }; Wave wave = { 0 }; - // NOTE: The entire file is loaded to memory to play it all at once (no-streaming) - - // Audio file loading - // NOTE: Buffer space is allocated inside function, Wave must be freed - - if (strcmp(GetExtension(fileName),"wav") == 0) wave = LoadWAV(fileName); - else if (strcmp(GetExtension(fileName),"ogg") == 0) wave = LoadOGG(fileName); - else - { - TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName); - - // TODO: Find a better way to register errors (similar to glGetError()) - lastAudioError = ERROR_EXTENSION_NOT_RECOGNIZED; - } - - if (wave.data != NULL) - { - ALenum format = 0; - // The OpenAL format is worked out by looking at the number of channels and the bits per sample - if (wave.channels == 1) - { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16; - } - else if (wave.channels == 2) - { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; - } - - // Create an audio source - ALuint source; - alGenSources(1, &source); // Generate pointer to audio source + if (strcmp(GetExtension(fileName), "wav") == 0) wave = LoadWAV(fileName); + else if (strcmp(GetExtension(fileName), "ogg") == 0) wave = LoadOGG(fileName); + else TraceLog(WARNING, "[%s] Sound extension not recognized, it can't be loaded", fileName); - alSourcef(source, AL_PITCH, 1); - alSourcef(source, AL_GAIN, 1); - alSource3f(source, AL_POSITION, 0, 0, 0); - alSource3f(source, AL_VELOCITY, 0, 0, 0); - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Convert loaded data to OpenAL buffer - //---------------------------------------- - ALuint buffer; - alGenBuffers(1, &buffer); // Generate pointer to buffer - - // Upload sound data to buffer - alBufferData(buffer, format, wave.data, wave.dataSize, wave.sampleRate); - - // Attach sound buffer to source - alSourcei(source, AL_BUFFER, buffer); - - TraceLog(INFO, "[%s] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", fileName, wave.sampleRate, wave.bitsPerSample, wave.channels); - - // Unallocate WAV data - UnloadWave(wave); - - sound.source = source; - sound.buffer = buffer; - } + Sound sound = LoadSoundFromWave(wave); + + // Sound is loaded, we can unload wave + UnloadWave(wave); return sound; } // Load sound from wave data +// NOTE: Wave data must be unallocated manually Sound LoadSoundFromWave(Wave wave) { Sound sound = { 0 }; @@ -586,10 +303,7 @@ Sound LoadSoundFromWave(Wave wave) // Attach sound buffer to source alSourcei(source, AL_BUFFER, buffer); - // Unallocate WAV data - UnloadWave(wave); - - TraceLog(INFO, "[Wave] Sound file loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", wave.sampleRate, wave.bitsPerSample, wave.channels); + TraceLog(INFO, "[SND ID %i][BUFR ID %i] Sound data loaded successfully (SampleRate: %i, BitRate: %i, Channels: %i)", source, buffer, wave.sampleRate, wave.bitsPerSample, wave.channels); sound.source = source; sound.buffer = buffer; @@ -619,11 +333,7 @@ Sound LoadSoundFromRES(const char *rresName, int resId) FILE *rresFile = fopen(rresName, "rb"); - if (rresFile == NULL) - { - TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); - lastAudioError = ERROR_UNABLE_TO_OPEN_RRES_FILE; - } + if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", rresName); else { // Read rres file (basic file check - id) @@ -637,7 +347,6 @@ Sound LoadSoundFromRES(const char *rresName, int resId) if ((id[0] != 'r') && (id[1] != 'R') && (id[2] != 'E') &&(id[3] != 'S')) { TraceLog(WARNING, "[%s] This is not a valid raylib resource file", rresName); - lastAudioError = ERROR_INVALID_RRES_FILE; } else { @@ -681,55 +390,12 @@ Sound LoadSoundFromRES(const char *rresName, int resId) free(data); - // Convert wave to Sound (OpenAL) - ALenum format = 0; - - // The OpenAL format is worked out by looking at the number of channels and the bits per sample - if (wave.channels == 1) - { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_MONO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_MONO16; - } - else if (wave.channels == 2) - { - if (wave.bitsPerSample == 8 ) format = AL_FORMAT_STEREO8; - else if (wave.bitsPerSample == 16) format = AL_FORMAT_STEREO16; - } - - // Create an audio source - ALuint source; - alGenSources(1, &source); // Generate pointer to audio source - - alSourcef(source, AL_PITCH, 1); - alSourcef(source, AL_GAIN, 1); - alSource3f(source, AL_POSITION, 0, 0, 0); - alSource3f(source, AL_VELOCITY, 0, 0, 0); - alSourcei(source, AL_LOOPING, AL_FALSE); - - // Convert loaded data to OpenAL buffer - //---------------------------------------- - ALuint buffer; - alGenBuffers(1, &buffer); // Generate pointer to buffer - - // Upload sound data to buffer - alBufferData(buffer, format, (void*)wave.data, wave.dataSize, wave.sampleRate); - - // Attach sound buffer to source - alSourcei(source, AL_BUFFER, buffer); - - TraceLog(INFO, "[%s] Sound loaded successfully from resource (SampleRate: %i, BitRate: %i, Channels: %i)", rresName, wave.sampleRate, wave.bitsPerSample, wave.channels); - - // Unallocate WAV data + sound = LoadSoundFromWave(wave); + + // Sound is loaded, we can unload wave data UnloadWave(wave); - - sound.source = source; - sound.buffer = buffer; - } - else - { - TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName); - lastAudioError = ERROR_INVALID_RRES_RESOURCE; } + else TraceLog(WARNING, "[%s] Required resource do not seem to be a valid SOUND resource", rresName); } else { @@ -764,7 +430,7 @@ void UnloadSound(Sound sound) alDeleteSources(1, &sound.source); alDeleteBuffers(1, &sound.buffer); - TraceLog(INFO, "Unloaded sound data"); + TraceLog(INFO, "[SND ID %i][BUFR ID %i] Unloaded sound data from RAM", sound.source, sound.buffer); } // Play a sound @@ -794,6 +460,16 @@ void PauseSound(Sound sound) alSourcePause(sound.source); } +// Resume a paused sound +void ResumeSound(Sound sound) +{ + ALenum state; + + alGetSourcei(sound.source, AL_SOURCE_STATE, &state); + + if (state == AL_PAUSED) alSourcePlay(sound.source); +} + // Stop reproducing a sound void StopSound(Sound sound) { @@ -828,409 +504,426 @@ void SetSoundPitch(Sound sound, float pitch) // Module Functions Definition - Music loading and stream playing (.OGG) //---------------------------------------------------------------------------------- -MusicBuffer LoadMusicBufferStream(char *fileName, int index) +// Load music stream from file +Music LoadMusicStream(char *fileName) { - MusicBuffer buffer = { 0 }; + Music music = (MusicData *)malloc(sizeof(MusicData)); - if(index > MAX_MUSIC_STREAMS) + if (strcmp(GetExtension(fileName), "ogg") == 0) { - TraceLog("[%s] index is greater than MAX_MUSIC_STREAMS", ERROR); - return; // error - } - - buffer.fileName = fileName; - buffer.index = index; - - - if (musicStreams[buffer.index].stream || musicStreams[buffer.index].xmctx) return; // error + // Open ogg audio stream + music->ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL); - return buffer; -} - -// Start music playing (open stream) -// returns 0 on success or error code -int PlayMusicStream(MusicBuffer musicBuffer) -{ - if(musicBuffer.fileName == 0) - { - return ERROR_UNINTIALIZED_MUSIC_BUFFER; - } - int mixIndex; - for (mixIndex = 0; mixIndex < MAX_MIX_CHANNELS; mixIndex++) // find empty mix channel slot - { - if (mixChannels[mixIndex] == NULL) break; - else if (mixIndex == (MAX_MIX_CHANNELS - 1)) return ERROR_OUT_OF_MIX_CHANNELS; // error - } - - if (strcmp(GetExtension(musicBuffer.fileName),"ogg") == 0) - { - // Open audio stream - musicStreams[musicBuffer.index].stream = stb_vorbis_open_filename(musicBuffer.fileName, NULL, NULL); - - if (musicStreams[musicBuffer.index].stream == NULL) + if (music->ctxOgg == NULL) { - TraceLog(WARNING, "[%s] OGG audio file could not be opened", musicBuffer.fileName); - return ERROR_LOADING_OGG; // error + TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName); } else { - // Get file info - stb_vorbis_info info = stb_vorbis_get_info(musicStreams[musicBuffer.index].stream); - - TraceLog(INFO, "[%s] Ogg sample rate: %i", musicBuffer.fileName, info.sample_rate); - TraceLog(INFO, "[%s] Ogg channels: %i", musicBuffer.fileName, info.channels); - TraceLog(DEBUG, "[%s] Temp memory required: %i", musicBuffer.fileName, info.temp_memory_required); + stb_vorbis_info info = stb_vorbis_get_info(music->ctxOgg); // Get Ogg file info - musicStreams[musicBuffer.index].loop = true; // We loop by default - musicStreams[musicBuffer.index].enabled = true; + TraceLog(DEBUG, "[%s] Ogg sample rate: %i", fileName, info.sample_rate); + TraceLog(DEBUG, "[%s] Ogg channels: %i", fileName, info.channels); + TraceLog(DEBUG, "[%s] Temp memory required: %i", fileName, info.temp_memory_required); + // TODO: Support 32-bit sampleSize OGGs + music->stream = InitAudioStream(info.sample_rate, 16, info.channels); + + music->totalSamples = (unsigned int)stb_vorbis_stream_length_in_samples(music->ctxOgg)*info.channels; + music->samplesLeft = music->totalSamples; + //float totalLengthSeconds = stb_vorbis_stream_length_in_seconds(music->ctxOgg); - musicStreams[musicBuffer.index].totalSamplesLeft = (unsigned int)stb_vorbis_stream_length_in_samples(musicStreams[musicBuffer.index].stream) * info.channels; - musicStreams[musicBuffer.index].totalLengthSeconds = stb_vorbis_stream_length_in_seconds(musicStreams[musicBuffer.index].stream); - - if (info.channels == 2) - { - musicStreams[musicBuffer.index].mixc = InitMixChannel(info.sample_rate, mixIndex, 2, false); - musicStreams[musicBuffer.index].mixc->playing = true; - } - else - { - musicStreams[musicBuffer.index].mixc = InitMixChannel(info.sample_rate, mixIndex, 1, false); - musicStreams[musicBuffer.index].mixc->playing = true; - } - - if (!musicStreams[musicBuffer.index].mixc) return ERROR_LOADING_OGG; // error + music->ctxType = MUSIC_AUDIO_OGG; + music->loop = true; // We loop by default } } - else if (strcmp(GetExtension(musicBuffer.fileName),"xm") == 0) + else if (strcmp(GetExtension(fileName), "xm") == 0) { - // only stereo is supported for xm - if (!jar_xm_create_context_from_file(&musicStreams[musicBuffer.index].xmctx, 48000, musicBuffer.fileName)) + int result = jar_xm_create_context_from_file(&music->ctxXm, 48000, fileName); + + if (!result) // XM context created successfully { - musicStreams[musicBuffer.index].chipTune = true; - musicStreams[musicBuffer.index].loop = true; - jar_xm_set_max_loop_count(musicStreams[musicBuffer.index].xmctx, 0); // infinite number of loops - musicStreams[musicBuffer.index].totalSamplesLeft = (unsigned int)jar_xm_get_remaining_samples(musicStreams[musicBuffer.index].xmctx); - musicStreams[musicBuffer.index].totalLengthSeconds = ((float)musicStreams[musicBuffer.index].totalSamplesLeft)/48000.0f; - musicStreams[musicBuffer.index].enabled = true; - - TraceLog(INFO, "[%s] XM number of samples: %i", musicBuffer.fileName, musicStreams[musicBuffer.index].totalSamplesLeft); - TraceLog(INFO, "[%s] XM track length: %11.6f sec", musicBuffer.fileName, musicStreams[musicBuffer.index].totalLengthSeconds); - - musicStreams[musicBuffer.index].mixc = InitMixChannel(48000, mixIndex, 2, true); - - if (!musicStreams[musicBuffer.index].mixc) return ERROR_XM_CONTEXT_CREATION; // error - - musicStreams[musicBuffer.index].mixc->playing = true; - } - else - { - TraceLog(WARNING, "[%s] XM file could not be opened", musicBuffer.fileName); - return ERROR_LOADING_XM; // error + jar_xm_set_max_loop_count(music->ctxXm, 0); // Set infinite number of loops + + music->totalSamples = (unsigned int)jar_xm_get_remaining_samples(music->ctxXm); + music->samplesLeft = music->totalSamples; + + TraceLog(INFO, "[%s] XM number of samples: %i", fileName, music->totalSamples); + TraceLog(INFO, "[%s] XM track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); + + // NOTE: Only stereo is supported for XM + music->stream = InitAudioStream(48000, 32, 2); + + music->ctxType = MUSIC_MODULE_XM; + music->loop = true; } + else TraceLog(WARNING, "[%s] XM file could not be opened", fileName); } - else if (strcmp(GetExtension(musicBuffer.fileName),"mod") == 0) + else if (strcmp(GetExtension(fileName), "mod") == 0) { - jar_mod_init(&musicStreams[musicBuffer.index].modctx); + jar_mod_init(&music->ctxMod); - if (jar_mod_load_file(&musicStreams[musicBuffer.index].modctx, musicBuffer.fileName)) + if (jar_mod_load_file(&music->ctxMod, fileName)) { - musicStreams[musicBuffer.index].chipTune = true; - musicStreams[musicBuffer.index].loop = true; - musicStreams[musicBuffer.index].totalSamplesLeft = (unsigned int)jar_mod_max_samples(&musicStreams[musicBuffer.index].modctx); - musicStreams[musicBuffer.index].totalLengthSeconds = ((float)musicStreams[musicBuffer.index].totalSamplesLeft)/48000.0f; - musicStreams[musicBuffer.index].enabled = true; + music->totalSamples = (unsigned int)jar_mod_max_samples(&music->ctxMod); + music->samplesLeft = music->totalSamples; - TraceLog(INFO, "[%s] MOD number of samples: %i", musicBuffer.fileName, musicStreams[musicBuffer.index].totalSamplesLeft); - TraceLog(INFO, "[%s] MOD track length: %11.6f sec", musicBuffer.fileName, musicStreams[musicBuffer.index].totalLengthSeconds); + TraceLog(INFO, "[%s] MOD number of samples: %i", fileName, music->samplesLeft); + TraceLog(INFO, "[%s] MOD track length: %11.6f sec", fileName, (float)music->totalSamples/48000.0f); - musicStreams[musicBuffer.index].mixc = InitMixChannel(48000, mixIndex, 2, false); + music->stream = InitAudioStream(48000, 16, 2); - if (!musicStreams[musicBuffer.index].mixc) return ERROR_MOD_CONTEXT_CREATION; // error - - musicStreams[musicBuffer.index].mixc->playing = true; - } - else - { - TraceLog(WARNING, "[%s] MOD file could not be opened", musicBuffer.fileName); - return ERROR_LOADING_MOD; // error + music->ctxType = MUSIC_MODULE_MOD; + music->loop = true; } + else TraceLog(WARNING, "[%s] MOD file could not be opened", fileName); } - else - { - TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", musicBuffer.fileName); - return ERROR_EXTENSION_NOT_RECOGNIZED; // error - } + else TraceLog(WARNING, "[%s] Music extension not recognized, it can't be loaded", fileName); - return 0; // normal return + return music; } -// Stop music playing for individual music index of musicStreams array (close stream) -void StopMusicStream(MusicBuffer musicBuffer) +// Unload music stream +void UnloadMusicStream(Music music) { - if (musicBuffer.index < MAX_MUSIC_STREAMS && musicStreams[musicBuffer.index].mixc) - { - CloseMixChannel(musicStreams[musicBuffer.index].mixc); - - if (musicStreams[musicBuffer.index].xmctx) - jar_xm_free_context(musicStreams[musicBuffer.index].xmctx); - else if (musicStreams[musicBuffer.index].modctx.mod_loaded) - jar_mod_unload(&musicStreams[musicBuffer.index].modctx); - else - stb_vorbis_close(musicStreams[musicBuffer.index].stream); - - musicStreams[musicBuffer.index].enabled = false; + CloseAudioStream(music->stream); + + if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg); + else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context(music->ctxXm); + else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod); + + free(music); +} - if (musicStreams[musicBuffer.index].stream || musicStreams[musicBuffer.index].xmctx) - { - musicStreams[musicBuffer.index].stream = NULL; - musicStreams[musicBuffer.index].xmctx = NULL; - } - } +// Start music playing (open stream) +void PlayMusicStream(Music music) +{ + alSourcePlay(music->stream.source); } -void StopMusicStreamEx(int index) +// Pause music playing +void PauseMusicStream(Music music) { - if (index < MAX_MUSIC_STREAMS && musicStreams[index].mixc) - { - CloseMixChannel(musicStreams[index].mixc); + alSourcePause(music->stream.source); +} - if (musicStreams[index].xmctx) - jar_xm_free_context(musicStreams[index].xmctx); - else if (musicStreams[index].modctx.mod_loaded) - jar_mod_unload(&musicStreams[index].modctx); - else - stb_vorbis_close(musicStreams[index].stream); +// Resume music playing +void ResumeMusicStream(Music music) +{ + ALenum state; + alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); - musicStreams[index].enabled = false; + if (state == AL_PAUSED) alSourcePlay(music->stream.source); +} - if (musicStreams[index].stream || musicStreams[index].xmctx) - { - musicStreams[index].stream = NULL; - musicStreams[index].xmctx = NULL; - } - } +// Stop music playing (close stream) +void StopMusicStream(Music music) +{ + alSourceStop(music->stream.source); } // Update (re-fill) music buffers if data already processed -void UpdateMusicStream(MusicBuffer musicBuffer) +void UpdateMusicStream(Music music) { ALenum state; bool active = true; ALint processed = 0; // Determine if music stream is ready to be written - alGetSourcei(musicStreams[musicBuffer.index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed); - if (musicStreams[musicBuffer.index].mixc->playing && (musicBuffer.index < MAX_MUSIC_STREAMS) && musicStreams[musicBuffer.index].enabled && musicStreams[musicBuffer.index].mixc && (processed > 0)) + if (processed > 0) { - active = BufferMusicStream(musicBuffer.index, processed); + active = BufferMusicStream(music, processed); - if (!active && musicStreams[musicBuffer.index].loop) + if (!active && music->loop) { - if (musicStreams[musicBuffer.index].chipTune) - { - if(musicStreams[musicBuffer.index].modctx.mod_loaded) jar_mod_seek_start(&musicStreams[musicBuffer.index].modctx); - - musicStreams[musicBuffer.index].totalSamplesLeft = musicStreams[musicBuffer.index].totalLengthSeconds*48000.0f; - } - else - { - stb_vorbis_seek_start(musicStreams[musicBuffer.index].stream); - musicStreams[musicBuffer.index].totalSamplesLeft = stb_vorbis_stream_length_in_samples(musicStreams[musicBuffer.index].stream)*musicStreams[musicBuffer.index].mixc->channels; - } + // Restart music context (if required) + if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_seek_start(&music->ctxMod); + else if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_seek_start(music->ctxOgg); + + music->samplesLeft = music->totalSamples; // Determine if music stream is ready to be written - alGetSourcei(musicStreams[musicBuffer.index].mixc->alSource, AL_BUFFERS_PROCESSED, &processed); + alGetSourcei(music->stream.source, AL_BUFFERS_PROCESSED, &processed); - active = BufferMusicStream(musicBuffer.index, processed); + active = BufferMusicStream(music, processed); } if (alGetError() != AL_NO_ERROR) TraceLog(WARNING, "Error buffering data..."); - alGetSourcei(musicStreams[musicBuffer.index].mixc->alSource, AL_SOURCE_STATE, &state); - - if (state != AL_PLAYING && active) alSourcePlay(musicStreams[musicBuffer.index].mixc->alSource); + alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); - if (!active) StopMusicStream(musicBuffer); + if (state != AL_PLAYING && active) alSourcePlay(music->stream.source); - } -} - -//get number of music channels active at this time, this does not mean they are playing -int GetMusicStreamCount(void) -{ - int musicCount = 0; - - // Find empty music slot - for (int musicIndex = 0; musicIndex < MAX_MUSIC_STREAMS; musicIndex++) - { - if(musicStreams[musicIndex].stream != NULL || musicStreams[musicIndex].chipTune) musicCount++; - } - - return musicCount; -} - -// Pause music playing -void PauseMusicStream(MusicBuffer musicBuffer) -{ - // Pause music stream if music available! - if (musicBuffer.index < MAX_MUSIC_STREAMS && musicStreams[musicBuffer.index].mixc && musicStreams[musicBuffer.index].enabled) - { - TraceLog(INFO, "Pausing music stream"); - alSourcePause(musicStreams[musicBuffer.index].mixc->alSource); - musicStreams[musicBuffer.index].mixc->playing = false; - } -} - -// Resume music playing -void ResumeMusicStream(MusicBuffer musicBuffer) -{ - // Resume music playing... if music available! - ALenum state; - - if (musicBuffer.index < MAX_MUSIC_STREAMS && musicStreams[musicBuffer.index].mixc) - { - alGetSourcei(musicStreams[musicBuffer.index].mixc->alSource, AL_SOURCE_STATE, &state); - - if (state == AL_PAUSED) - { - TraceLog(INFO, "Resuming music stream"); - alSourcePlay(musicStreams[musicBuffer.index].mixc->alSource); - musicStreams[musicBuffer.index].mixc->playing = true; - } + if (!active) StopMusicStream(music); } } // Check if any music is playing -bool IsMusicPlaying(MusicBuffer musicBuffer) +bool IsMusicPlaying(Music music) { bool playing = false; ALint state; - if (musicBuffer.index < MAX_MUSIC_STREAMS && musicStreams[musicBuffer.index].mixc) - { - alGetSourcei(musicStreams[musicBuffer.index].mixc->alSource, AL_SOURCE_STATE, &state); + alGetSourcei(music->stream.source, AL_SOURCE_STATE, &state); - if (state == AL_PLAYING) playing = true; - } + if (state == AL_PLAYING) playing = true; return playing; } // Set volume for music -void SetMusicVolume(MusicBuffer musicBuffer, float volume) +void SetMusicVolume(Music music, float volume) { - if (musicBuffer.index < MAX_MUSIC_STREAMS && musicStreams[musicBuffer.index].mixc) - { - alSourcef(musicStreams[musicBuffer.index].mixc->alSource, AL_GAIN, volume); - } + alSourcef(music->stream.source, AL_GAIN, volume); } // Set pitch for music -void SetMusicPitch(MusicBuffer musicBuffer, float pitch) +void SetMusicPitch(Music music, float pitch) { - if (musicBuffer.index < MAX_MUSIC_STREAMS && musicStreams[musicBuffer.index].mixc) - { - alSourcef(musicStreams[musicBuffer.index].mixc->alSource, AL_PITCH, pitch); - } + alSourcef(music->stream.source, AL_PITCH, pitch); } // Get music time length (in seconds) -float GetMusicTimeLength(MusicBuffer musicBuffer) +float GetMusicTimeLength(Music music) { - float totalSeconds; - - if (musicStreams[musicBuffer.index].chipTune) totalSeconds = (float)musicStreams[musicBuffer.index].totalLengthSeconds; - else totalSeconds = stb_vorbis_stream_length_in_seconds(musicStreams[musicBuffer.index].stream); - + float totalSeconds = (float)music->totalSamples/music->stream.sampleRate; + return totalSeconds; } // Get current music time played (in seconds) -float GetMusicTimePlayed(MusicBuffer musicBuffer) +float GetMusicTimePlayed(Music music) { float secondsPlayed = 0.0f; - if (musicBuffer.index < MAX_MUSIC_STREAMS && musicStreams[musicBuffer.index].mixc) + if (music->ctxType == MUSIC_MODULE_XM) { - if (musicStreams[musicBuffer.index].chipTune && musicStreams[musicBuffer.index].xmctx) + uint64_t samplesPlayed; + jar_xm_get_position(music->ctxXm, NULL, NULL, NULL, &samplesPlayed); + + // TODO: Not sure if this is the correct value + secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels); + } + else if (music->ctxType == MUSIC_MODULE_MOD) + { + long samplesPlayed = jar_mod_current_samples(&music->ctxMod); + + secondsPlayed = (float)samplesPlayed/music->stream.sampleRate; + } + else if (music->ctxType == MUSIC_AUDIO_OGG) + { + unsigned int samplesPlayed = music->totalSamples - music->samplesLeft; + + secondsPlayed = (float)samplesPlayed/(music->stream.sampleRate*music->stream.channels); + } + + return secondsPlayed; +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// Init audio stream (to stream audio pcm data) +static AudioStream InitAudioStream(unsigned int sampleRate, unsigned int sampleSize, unsigned int channels) +{ + AudioStream stream = { 0 }; + + stream.sampleRate = sampleRate; + stream.sampleSize = sampleSize; + stream.channels = channels; + + // Setup OpenAL format + if (channels == 1) + { + switch (sampleSize) { - uint64_t samples; - jar_xm_get_position(musicStreams[musicBuffer.index].xmctx, NULL, NULL, NULL, &samples); - secondsPlayed = (float)samples/(48000.0f*musicStreams[musicBuffer.index].mixc->channels); // Not sure if this is the correct value + case 8: stream.format = AL_FORMAT_MONO8; break; + case 16: stream.format = AL_FORMAT_MONO16; break; + case 32: stream.format = AL_FORMAT_MONO_FLOAT32; break; + default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; } - else if(musicStreams[musicBuffer.index].chipTune && musicStreams[musicBuffer.index].modctx.mod_loaded) + } + else if (channels == 2) + { + switch (sampleSize) { - long numsamp = jar_mod_current_samples(&musicStreams[musicBuffer.index].modctx); - secondsPlayed = (float)numsamp/(48000.0f); + case 8: stream.format = AL_FORMAT_STEREO8; break; + case 16: stream.format = AL_FORMAT_STEREO16; break; + case 32: stream.format = AL_FORMAT_STEREO_FLOAT32; break; + default: TraceLog(WARNING, "Init audio stream: Sample size not supported: %i", sampleSize); break; } - else + } + else TraceLog(WARNING, "Init audio stream: Number of channels not supported: %i", channels); + + // Create an audio source + alGenSources(1, &stream.source); + alSourcef(stream.source, AL_PITCH, 1); + alSourcef(stream.source, AL_GAIN, 1); + alSource3f(stream.source, AL_POSITION, 0, 0, 0); + alSource3f(stream.source, AL_VELOCITY, 0, 0, 0); + + // Create Buffers + alGenBuffers(MAX_STREAM_BUFFERS, stream.buffers); + + // Initialize buffer with zeros by default + for (int i = 0; i < MAX_STREAM_BUFFERS; i++) + { + if (stream.sampleSize == 8) { - int totalSamples = stb_vorbis_stream_length_in_samples(musicStreams[musicBuffer.index].stream)*musicStreams[musicBuffer.index].mixc->channels; - int samplesPlayed = totalSamples - musicStreams[musicBuffer.index].totalSamplesLeft; - secondsPlayed = (float)samplesPlayed/(musicStreams[musicBuffer.index].mixc->sampleRate*musicStreams[musicBuffer.index].mixc->channels); + unsigned char pcm[AUDIO_BUFFER_SIZE] = { 0 }; + alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(unsigned char), stream.sampleRate); + } + else if (stream.sampleSize == 16) + { + short pcm[AUDIO_BUFFER_SIZE] = { 0 }; + alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(short), stream.sampleRate); + } + else if (stream.sampleSize == 32) + { + float pcm[AUDIO_BUFFER_SIZE] = { 0.0f }; + alBufferData(stream.buffers[i], stream.format, pcm, AUDIO_BUFFER_SIZE*sizeof(float), stream.sampleRate); } } - return secondsPlayed; + alSourceQueueBuffers(stream.source, MAX_STREAM_BUFFERS, stream.buffers); + + TraceLog(INFO, "[AUD ID %i] Audio stream loaded successfully", stream.source); + + return stream; } -//---------------------------------------------------------------------------------- -// Module specific Functions Definition -//---------------------------------------------------------------------------------- +// Close audio stream and free memory +static void CloseAudioStream(AudioStream stream) +{ + // Stop playing channel + alSourceStop(stream.source); + + // Flush out all queued buffers + int queued = 0; + alGetSourcei(stream.source, AL_BUFFERS_QUEUED, &queued); + + ALuint buffer = 0; + + while (queued > 0) + { + alSourceUnqueueBuffers(stream.source, 1, &buffer); + queued--; + } + + // Delete source and buffers + alDeleteSources(1, &stream.source); + alDeleteBuffers(MAX_STREAM_BUFFERS, stream.buffers); + + TraceLog(INFO, "[AUD ID %i] Unloaded audio stream data", stream.source); +} + +// Push more audio data into audio stream, only one buffer per call +// NOTE: Returns number of samples that were processed +static int BufferAudioStream(AudioStream stream, void *data, int numberElements) +{ + if (!data || !numberElements) + { + // Pauses audio until data is given + alSourcePause(stream.source); + return 0; + } + + ALuint buffer = 0; + alSourceUnqueueBuffers(stream.source, 1, &buffer); + + if (!buffer) return 0; + + // Reference + //void alBufferData(ALuint bufferName, ALenum format, const ALvoid *data, ALsizei size, ALsizei frequency); + + // ALuint bufferName: buffer id + // ALenum format: Valid formats are + // AL_FORMAT_MONO8, // unsigned char + // AL_FORMAT_MONO16, // short + // AL_FORMAT_STEREO8, + // AL_FORMAT_STEREO16 // stereo data is interleaved: left+right channels sample + // AL_FORMAT_MONO_FLOAT32 (extension) + // AL_FORMAT_STEREO_FLOAT32 (extension) + // ALsizei size: Number of bytes, must be coherent with format + // ALsizei frequency: sample rate + + if (stream.sampleSize == 8) alBufferData(buffer, stream.format, (unsigned char *)data, numberElements*sizeof(unsigned char), stream.sampleRate); + else if (stream.sampleSize == 16) alBufferData(buffer, stream.format, (short *)data, numberElements*sizeof(short), stream.sampleRate); + else if (stream.sampleSize == 32) alBufferData(buffer, stream.format, (float *)data, numberElements*sizeof(float), stream.sampleRate); + + alSourceQueueBuffers(stream.source, 1, &buffer); + + return numberElements; +} // Fill music buffers with new data from music stream -static bool BufferMusicStream(int index, int numBuffers) +static bool BufferMusicStream(Music music, int numBuffersToProcess) { - short pcm[MUSIC_BUFFER_SIZE_SHORT]; - float pcmf[MUSIC_BUFFER_SIZE_FLOAT]; + short pcm[AUDIO_BUFFER_SIZE]; + float pcmf[AUDIO_BUFFER_SIZE]; int size = 0; // Total size of data steamed in L+R samples for xm floats, individual L or R for ogg shorts bool active = true; // We can get more data from stream (not finished) - if (musicStreams[index].chipTune) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. + if (music->ctxType == MUSIC_MODULE_XM) // There is no end of stream for xmfiles, once the end is reached zeros are generated for non looped chiptunes. { - for (int i = 0; i < numBuffers; i++) + for (int i = 0; i < numBuffersToProcess; i++) { - if (musicStreams[index].modctx.mod_loaded) - { - if (musicStreams[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) size = MUSIC_BUFFER_SIZE_SHORT/2; - else size = musicStreams[index].totalSamplesLeft/2; + if (music->samplesLeft >= AUDIO_BUFFER_SIZE) size = AUDIO_BUFFER_SIZE/2; + else size = music->samplesLeft/2; - jar_mod_fillbuffer(&musicStreams[index].modctx, pcm, size, 0 ); - BufferMixChannel(musicStreams[index].mixc, pcm, size*2); - } - else if (musicStreams[index].xmctx) - { - if (musicStreams[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_FLOAT) size = MUSIC_BUFFER_SIZE_FLOAT/2; - else size = musicStreams[index].totalSamplesLeft/2; + // Read 2*shorts and moves them to buffer+size memory location + jar_xm_generate_samples(music->ctxXm, pcmf, size); + + BufferAudioStream(music->stream, pcmf, size*2); + + music->samplesLeft -= size; - jar_xm_generate_samples(musicStreams[index].xmctx, pcmf, size); // reads 2*readlen shorts and moves them to buffer+size memory location - BufferMixChannel(musicStreams[index].mixc, pcmf, size*2); + if (music->samplesLeft <= 0) + { + active = false; + break; } + } + } + else if (music->ctxType == MUSIC_MODULE_MOD) + { + for (int i = 0; i < numBuffersToProcess; i++) + { + if (music->samplesLeft >= AUDIO_BUFFER_SIZE) size = AUDIO_BUFFER_SIZE/2; + else size = music->samplesLeft/2; + + jar_mod_fillbuffer(&music->ctxMod, pcm, size, 0); + + BufferAudioStream(music->stream, pcm, size*2); - musicStreams[index].totalSamplesLeft -= size; + music->samplesLeft -= size; - if (musicStreams[index].totalSamplesLeft <= 0) + if (music->samplesLeft <= 0) { active = false; break; } } } - else + else if (music->ctxType == MUSIC_AUDIO_OGG) { - if (musicStreams[index].totalSamplesLeft >= MUSIC_BUFFER_SIZE_SHORT) size = MUSIC_BUFFER_SIZE_SHORT; - else size = musicStreams[index].totalSamplesLeft; + if (music->samplesLeft >= AUDIO_BUFFER_SIZE) size = AUDIO_BUFFER_SIZE; + else size = music->samplesLeft; - for (int i = 0; i < numBuffers; i++) + for (int i = 0; i < numBuffersToProcess; i++) { - int streamedBytes = stb_vorbis_get_samples_short_interleaved(musicStreams[index].stream, musicStreams[index].mixc->channels, pcm, size); - BufferMixChannel(musicStreams[index].mixc, pcm, streamedBytes * musicStreams[index].mixc->channels); - musicStreams[index].totalSamplesLeft -= streamedBytes * musicStreams[index].mixc->channels; - - if (musicStreams[index].totalSamplesLeft <= 0) + // NOTE: Returns the number of samples stored per channel + int numSamples = stb_vorbis_get_samples_short_interleaved(music->ctxOgg, music->stream.channels, pcm, size); + + BufferAudioStream(music->stream, pcm, numSamples*music->stream.channels); + + music->samplesLeft -= (numSamples*music->stream.channels); + + if (music->samplesLeft <= 0) { active = false; break; @@ -1241,22 +934,6 @@ static bool BufferMusicStream(int index, int numBuffers) return active; } -// Empty music buffers -static void EmptyMusicStream(int index) -{ - ALuint buffer = 0; - int queued = 0; - - alGetSourcei(musicStreams[index].mixc->alSource, AL_BUFFERS_QUEUED, &queued); - - while (queued > 0) - { - alSourceUnqueueBuffers(musicStreams[index].mixc->alSource, 1, &buffer); - - queued--; - } -} - // Load WAV file into Wave structure static Wave LoadWAV(const char *fileName) { @@ -1382,7 +1059,7 @@ static Wave LoadOGG(char *fileName) TraceLog(DEBUG, "[%s] Ogg sample rate: %i", fileName, info.sample_rate); TraceLog(DEBUG, "[%s] Ogg channels: %i", fileName, info.channels); - int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile) * info.channels); + int totalSamplesLength = (stb_vorbis_stream_length_in_samples(oggFile)*info.channels); wave.dataSize = totalSamplesLength*sizeof(short); // Size must be in bytes @@ -1417,7 +1094,7 @@ static void UnloadWave(Wave wave) { free(wave.data); - TraceLog(INFO, "Unloaded wave data"); + TraceLog(INFO, "Unloaded wave data from RAM"); } // Some required functions for audio standalone module version diff --git a/src/audio.h b/src/audio.h index d39162b5..c9171339 100644 --- a/src/audio.h +++ b/src/audio.h @@ -75,10 +75,9 @@ typedef struct Wave { short channels; } Wave; -typedef struct MusicBuffer { - char *fileName; - int index; // index in musicStreams -} MusicBuffer; +// Music type (file streaming from memory) +// NOTE: Anything longer than ~10 seconds should be streamed into a mix channel... +typedef struct Music *Music; #ifdef __cplusplus extern "C" { // Prevents name mangling of functions @@ -102,27 +101,24 @@ Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to void UnloadSound(Sound sound); // Unload sound void PlaySound(Sound sound); // Play a sound void PauseSound(Sound sound); // Pause a sound +void ResumeSound(Sound sound); // Resume a paused sound void StopSound(Sound sound); // Stop playing a sound bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -MusicBuffer LoadMusicBufferStream(char *fileName, int index); -int PlayMusicStream(MusicBuffer buffer); // Start music playing (open stream) -void UpdateMusicStream(MusicBuffer buffer); // Updates buffers for music streaming -void StopMusicStream(MusicBuffer buffer); // Stop music playing (close stream) -void PauseMusicStream(MusicBuffer buffer); // Pause music playing -void ResumeMusicStream(MusicBuffer buffer); // Resume playing paused music -bool IsMusicPlaying(MusicBuffer buffer); // Check if music is playing -void SetMusicVolume(MusicBuffer buffer float volume); // Set volume for music (1.0 is max level) -void SetMusicPitch(MusicBuffer buffer, float pitch); // Set pitch for a music (1.0 is base level) -float GetMusicTimeLength(MusicBuffer buffer); // Get music time length (in seconds) -float GetMusicTimePlayed(MusicBuffer buffer); // Get current music time played (in seconds) -int GetMusicStreamCount(void); // Get number of streams loaded - -int InitRawMixChannel(int sampleRate, int channels, bool floatingPoint); // Initialize raw audio mix channel for audio buffering -int BufferRawMixChannel(int mixc, void *data, unsigned short numberElements); // Buffers data directly to raw mix channel -void CloseRawMixChannel(int mixc); // Closes and frees raw mix channel +Music LoadMusicStream(char *fileName); // Load music stream from file +void UnloadMusicStream(Music music); // Unload music stream +void PlayMusicStream(Music music); // Start music playing (open stream) +void UpdateMusicStream(Music music); // Updates buffers for music streaming +void StopMusicStream(Music music); // Stop music playing (close stream) +void PauseMusicStream(Music music); // Pause music playing +void ResumeMusicStream(Music music); // Resume playing paused music +bool IsMusicPlaying(Music music); // Check if music is playing +void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) +void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) +float GetMusicTimeLength(Music music); // Get music time length (in seconds) +float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) #ifdef __cplusplus } diff --git a/src/external/jar_xm.h b/src/external/jar_xm.h index 02463e08..7f0517df 100644 --- a/src/external/jar_xm.h +++ b/src/external/jar_xm.h @@ -102,7 +102,7 @@ int jar_xm_create_context_from_file(jar_xm_context_t** ctx, uint32_t rate, const * @deprecated This function is unsafe! * @see jar_xm_create_context_safe() */ -int jar_xm_create_context(jar_xm_context_t**, const char* moddata, uint32_t rate); +int jar_xm_create_context(jar_xm_context_t** ctx, const char* moddata, uint32_t rate); /** Create a XM context. * @@ -114,17 +114,17 @@ int jar_xm_create_context(jar_xm_context_t**, const char* moddata, uint32_t rate * @returns 1 if module data is not sane * @returns 2 if memory allocation failed */ -int jar_xm_create_context_safe(jar_xm_context_t**, const char* moddata, size_t moddata_length, uint32_t rate); +int jar_xm_create_context_safe(jar_xm_context_t** ctx, const char* moddata, size_t moddata_length, uint32_t rate); /** Free a XM context created by jar_xm_create_context(). */ -void jar_xm_free_context(jar_xm_context_t*); +void jar_xm_free_context(jar_xm_context_t* ctx); /** Play the module and put the sound samples in an output buffer. * * @param output buffer of 2*numsamples elements (A left and right value for each sample) * @param numsamples number of samples to generate */ -void jar_xm_generate_samples(jar_xm_context_t*, float* output, size_t numsamples); +void jar_xm_generate_samples(jar_xm_context_t* ctx, float* output, size_t numsamples); /** Play the module, resample from 32 bit to 16 bit, and put the sound samples in an output buffer. * @@ -173,12 +173,12 @@ void jar_xm_generate_samples_8bit(jar_xm_context_t* ctx, char* output, size_t nu * * @param loopcnt maximum number of loops. Use 0 to loop * indefinitely. */ -void jar_xm_set_max_loop_count(jar_xm_context_t*, uint8_t loopcnt); +void jar_xm_set_max_loop_count(jar_xm_context_t* ctx, uint8_t loopcnt); /** Get the loop count of the currently playing module. This value is * 0 when the module is still playing, 1 when the module has looped * once, etc. */ -uint8_t jar_xm_get_loop_count(jar_xm_context_t*); +uint8_t jar_xm_get_loop_count(jar_xm_context_t* ctx); @@ -188,7 +188,7 @@ uint8_t jar_xm_get_loop_count(jar_xm_context_t*); * * @return whether the channel was muted. */ -bool jar_xm_mute_channel(jar_xm_context_t*, uint16_t, bool); +bool jar_xm_mute_channel(jar_xm_context_t* ctx, uint16_t, bool); /** Mute or unmute an instrument. * @@ -197,43 +197,43 @@ bool jar_xm_mute_channel(jar_xm_context_t*, uint16_t, bool); * * @return whether the instrument was muted. */ -bool jar_xm_mute_instrument(jar_xm_context_t*, uint16_t, bool); +bool jar_xm_mute_instrument(jar_xm_context_t* ctx, uint16_t, bool); /** Get the module name as a NUL-terminated string. */ -const char* jar_xm_get_module_name(jar_xm_context_t*); +const char* jar_xm_get_module_name(jar_xm_context_t* ctx); /** Get the tracker name as a NUL-terminated string. */ -const char* jar_xm_get_tracker_name(jar_xm_context_t*); +const char* jar_xm_get_tracker_name(jar_xm_context_t* ctx); /** Get the number of channels. */ -uint16_t jar_xm_get_number_of_channels(jar_xm_context_t*); +uint16_t jar_xm_get_number_of_channels(jar_xm_context_t* ctx); /** Get the module length (in patterns). */ uint16_t jar_xm_get_module_length(jar_xm_context_t*); /** Get the number of patterns. */ -uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t*); +uint16_t jar_xm_get_number_of_patterns(jar_xm_context_t* ctx); /** Get the number of rows of a pattern. * * @note Pattern numbers go from 0 to * jar_xm_get_number_of_patterns(...)-1. */ -uint16_t jar_xm_get_number_of_rows(jar_xm_context_t*, uint16_t); +uint16_t jar_xm_get_number_of_rows(jar_xm_context_t* ctx, uint16_t); /** Get the number of instruments. */ -uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t*); +uint16_t jar_xm_get_number_of_instruments(jar_xm_context_t* ctx); /** Get the number of samples of an instrument. * * @note Instrument numbers go from 1 to * jar_xm_get_number_of_instruments(...). */ -uint16_t jar_xm_get_number_of_samples(jar_xm_context_t*, uint16_t); +uint16_t jar_xm_get_number_of_samples(jar_xm_context_t* ctx, uint16_t); @@ -242,7 +242,7 @@ uint16_t jar_xm_get_number_of_samples(jar_xm_context_t*, uint16_t); * @param bpm will receive the current BPM * @param tempo will receive the current tempo (ticks per line) */ -void jar_xm_get_playing_speed(jar_xm_context_t*, uint16_t* bpm, uint16_t* tempo); +void jar_xm_get_playing_speed(jar_xm_context_t* ctx, uint16_t* bpm, uint16_t* tempo); /** Get the current position in the module being played. * @@ -257,7 +257,7 @@ void jar_xm_get_playing_speed(jar_xm_context_t*, uint16_t* bpm, uint16_t* tempo) * generated samples (divide by sample rate to get seconds of * generated audio) */ -void jar_xm_get_position(jar_xm_context_t*, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples); +void jar_xm_get_position(jar_xm_context_t* ctx, uint8_t* pattern_index, uint8_t* pattern, uint8_t* row, uint64_t* samples); /** Get the latest time (in number of generated samples) when a * particular instrument was triggered in any channel. @@ -265,7 +265,7 @@ void jar_xm_get_position(jar_xm_context_t*, uint8_t* pattern_index, uint8_t* pat * @note Instrument numbers go from 1 to * jar_xm_get_number_of_instruments(...). */ -uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t*, uint16_t); +uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t* ctx, uint16_t); /** Get the latest time (in number of generated samples) when a * particular sample was triggered in any channel. @@ -276,21 +276,21 @@ uint64_t jar_xm_get_latest_trigger_of_instrument(jar_xm_context_t*, uint16_t); * @note Sample numbers go from 0 to * jar_xm_get_nubmer_of_samples(...,instr)-1. */ -uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t*, uint16_t instr, uint16_t sample); +uint64_t jar_xm_get_latest_trigger_of_sample(jar_xm_context_t* ctx, uint16_t instr, uint16_t sample); /** Get the latest time (in number of generated samples) when any * instrument was triggered in a given channel. * * @note Channel numbers go from 1 to jar_xm_get_number_of_channels(...). */ -uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t*, uint16_t); +uint64_t jar_xm_get_latest_trigger_of_channel(jar_xm_context_t* ctx, uint16_t); /** Get the number of remaining samples. Divide by 2 to get the number of individual LR data samples. * * @note This is the remaining number of samples before the loop starts module again, or halts if on last pass. * @note This function is very slow and should only be run once, if at all. */ -uint64_t jar_xm_get_remaining_samples(jar_xm_context_t*); +uint64_t jar_xm_get_remaining_samples(jar_xm_context_t* ctx); #ifdef __cplusplus } @@ -308,7 +308,7 @@ uint64_t jar_xm_get_remaining_samples(jar_xm_context_t*); #include #include -#if JAR_XM_DEBUG +#ifdef JAR_XM_DEBUG #include #define DEBUG(fmt, ...) do { \ fprintf(stderr, "%s(): " fmt "\n", __func__, __VA_ARGS__); \ @@ -638,7 +638,7 @@ int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, siz /* Initialize most of the fields to 0, 0.f, NULL or false depending on type */ memset(mempool, 0, bytes_needed); - ctx = (*ctxp = (jar_xm_context_t*)mempool); + ctx = (*ctxp = (jar_xm_context_t *)mempool); ctx->allocated_memory = mempool; /* Keep original pointer for free() */ mempool += sizeof(jar_xm_context_t); @@ -685,20 +685,18 @@ int jar_xm_create_context_safe(jar_xm_context_t** ctxp, const char* moddata, siz return 0; } -void jar_xm_free_context(jar_xm_context_t* context) { - free(context->allocated_memory); +void jar_xm_free_context(jar_xm_context_t* ctx) { + free(ctx->allocated_memory); } -void jar_xm_set_max_loop_count(jar_xm_context_t* context, uint8_t loopcnt) { - context->max_loop_count = loopcnt; +void jar_xm_set_max_loop_count(jar_xm_context_t* ctx, uint8_t loopcnt) { + ctx->max_loop_count = loopcnt; } -uint8_t jar_xm_get_loop_count(jar_xm_context_t* context) { - return context->loop_count; +uint8_t jar_xm_get_loop_count(jar_xm_context_t* ctx) { + return ctx->loop_count; } - - bool jar_xm_mute_channel(jar_xm_context_t* ctx, uint16_t channel, bool mute) { bool old = ctx->channels[channel - 1].muted; ctx->channels[channel - 1].muted = mute; diff --git a/src/raylib.h b/src/raylib.h index 1966f75e..f8dd8359 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -478,10 +478,9 @@ typedef struct Wave { short channels; } Wave; -typedef struct MusicBuffer { - char *fileName; - int index; // index in musicStreams -} MusicBuffer; +// Music type (file streaming from memory) +// NOTE: Anything longer than ~10 seconds should be streamed +typedef struct Music *Music; // Texture formats // NOTE: Support depends on OpenGL version and platform @@ -897,23 +896,24 @@ Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to void UnloadSound(Sound sound); // Unload sound void PlaySound(Sound sound); // Play a sound void PauseSound(Sound sound); // Pause a sound +void ResumeSound(Sound sound); // Resume a paused sound void StopSound(Sound sound); // Stop playing a sound bool IsSoundPlaying(Sound sound); // Check if a sound is currently playing void SetSoundVolume(Sound sound, float volume); // Set volume for a sound (1.0 is max level) void SetSoundPitch(Sound sound, float pitch); // Set pitch for a sound (1.0 is base level) -MusicBuffer LoadMusicBufferStream(char *fileName, int index); -int PlayMusicStream(MusicBuffer buffer); // Start music playing (open stream) -void UpdateMusicStream(MusicBuffer buffer); // Updates buffers for music streaming -void StopMusicStream(MusicBuffer buffer); // Stop music playing (close stream) -void PauseMusicStream(MusicBuffer buffer); // Pause music playing -void ResumeMusicStream(MusicBuffer buffer); // Resume playing paused music -bool IsMusicPlaying(MusicBuffer buffer); // Check if music is playing -void SetMusicVolume(MusicBuffer buffer, float volume); // Set volume for music (1.0 is max level) -void SetMusicPitch(MusicBuffer buffer, float pitch); // Set pitch for a music (1.0 is base level) -float GetMusicTimeLength(MusicBuffer buffer); // Get music time length (in seconds) -float GetMusicTimePlayed(MusicBuffer buffer); // Get current music time played (in seconds) -int GetMusicStreamCount(void); // Get number of streams loaded +Music LoadMusicStream(char *fileName); // Load music stream from file +void UnloadMusicStream(Music music); // Unload music stream +void PlayMusicStream(Music music); // Start music playing (open stream) +void UpdateMusicStream(Music music); // Updates buffers for music streaming +void StopMusicStream(Music music); // Stop music playing (close stream) +void PauseMusicStream(Music music); // Pause music playing +void ResumeMusicStream(Music music); // Resume playing paused music +bool IsMusicPlaying(Music music); // Check if music is playing +void SetMusicVolume(Music music, float volume); // Set volume for music (1.0 is max level) +void SetMusicPitch(Music music, float pitch); // Set pitch for a music (1.0 is base level) +float GetMusicTimeLength(Music music); // Get music time length (in seconds) +float GetMusicTimePlayed(Music music); // Get current music time played (in seconds) #ifdef __cplusplus } -- cgit v1.2.3 From 735968e68543bb5141122f181608d0d7ada9a3be Mon Sep 17 00:00:00 2001 From: raysan5 Date: Wed, 3 Aug 2016 21:38:21 +0200 Subject: [rlua] new module: raylib Lua binding --- examples/core_basic_window.lua | 41 + examples/rlua_execute_file.c | 39 + src/external/lua/include/lauxlib.h | 256 +++ src/external/lua/include/lua.h | 486 +++++ src/external/lua/include/lua.hpp | 9 + src/external/lua/include/luaconf.h | 769 ++++++++ src/external/lua/include/lualib.h | 58 + src/external/lua/lib/liblua53.a | Bin 0 -> 322424 bytes src/external/lua/lib/liblua53dll.a | Bin 0 -> 91416 bytes src/rlua.h | 3631 ++++++++++++++++++++++++++++++++++++ 10 files changed, 5289 insertions(+) create mode 100644 examples/core_basic_window.lua create mode 100644 examples/rlua_execute_file.c create mode 100644 src/external/lua/include/lauxlib.h create mode 100644 src/external/lua/include/lua.h create mode 100644 src/external/lua/include/lua.hpp create mode 100644 src/external/lua/include/luaconf.h create mode 100644 src/external/lua/include/lualib.h create mode 100644 src/external/lua/lib/liblua53.a create mode 100644 src/external/lua/lib/liblua53dll.a create mode 100644 src/rlua.h (limited to 'src/external') diff --git a/examples/core_basic_window.lua b/examples/core_basic_window.lua new file mode 100644 index 00000000..4f288005 --- /dev/null +++ b/examples/core_basic_window.lua @@ -0,0 +1,41 @@ +-- Initialization +---------------------------------------------------------------------------------------- +local screenWidth = 800 +local screenHeight = 450 + +InitWindow(screenWidth, screenHeight, "raylib [rlua] example - basic window") + +InitAudioDevice() + +--local pause = false +--local texture = LoadTexture("resources/texture.png") + +SetTargetFPS(60) -- Set target frames-per-second +---------------------------------------------------------------------------------------- + +while not WindowShouldClose() do + -- Update + ------------------------------------------------------------------------------------ + --if (IsKeyPressed(KEY.SPACE)) then + -- pause = not pause + --end + ------------------------------------------------------------------------------------ + + -- Draw + ------------------------------------------------------------------------------------ + BeginDrawing() + + ClearBackground(RAYWHITE) + + DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY) + + EndDrawing() + ------------------------------------------------------------------------------------ +end + +-- De-Initialization +------------------------------------------------------------------------------------ +CloseAudioDevice() -- Close audio device + +CloseWindow() -- Close window and OpenGL context +------------------------------------------------------------------------------------ \ No newline at end of file diff --git a/examples/rlua_execute_file.c b/examples/rlua_execute_file.c new file mode 100644 index 00000000..b21a4459 --- /dev/null +++ b/examples/rlua_execute_file.c @@ -0,0 +1,39 @@ +/******************************************************************************************* +* +* raylib [rlua] example - Lua file execution +* +* NOTE: This example requires Lua library (http://luabinaries.sourceforge.net/download.html) +* +* Compile example using: +* gcc -o $(NAME_PART).exe $(FILE_NAME) $(RAYLIB_DIR)\raylib_icon / +* -I../src -I../src/external/lua/include -L../src/external/lua/lib / +* -lraylib -lglfw3 -lopengl32 -lopenal32 -llua53 -lgdi32 -std=c99 +* +* This example has been created using raylib 1.6 (www.raylib.com) +* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +* +* Copyright (c) 2013-2016 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define RLUA_IMPLEMENTATION +#include "rlua.h" + +int main() +{ + // Initialization + //-------------------------------------------------------------------------------------- + InitLuaDevice(); + //-------------------------------------------------------------------------------------- + + ExecuteLuaFile("core_basic_window.lua"); + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseLuaDevice(); // Close Lua device and free resources + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/src/external/lua/include/lauxlib.h b/src/external/lua/include/lauxlib.h new file mode 100644 index 00000000..ddb7c228 --- /dev/null +++ b/src/external/lua/include/lauxlib.h @@ -0,0 +1,256 @@ +/* +** $Id: lauxlib.h,v 1.129 2015/11/23 11:29:43 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#include + +#include "lua.h" + + + +/* extra error code for 'luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + +#define LUAL_NUMSIZES (sizeof(lua_Integer)*16 + sizeof(lua_Number)) + +LUALIB_API void (luaL_checkversion_) (lua_State *L, lua_Number ver, size_t sz); +#define luaL_checkversion(L) \ + luaL_checkversion_(L, LUA_VERSION_NUM, LUAL_NUMSIZES) + +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API const char *(luaL_tolstring) (lua_State *L, int idx, size_t *len); +LUALIB_API int (luaL_argerror) (lua_State *L, int arg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int arg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int arg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int arg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int arg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int arg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int arg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int arg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int arg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void (luaL_setmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int arg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_fileresult) (lua_State *L, int stat, const char *fname); +LUALIB_API int (luaL_execresult) (lua_State *L, int stat); + +/* predefined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfilex) (lua_State *L, const char *filename, + const char *mode); + +#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) + +LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + +LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API void (luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); + +LUALIB_API int (luaL_getsubtable) (lua_State *L, int idx, const char *fname); + +LUALIB_API void (luaL_traceback) (lua_State *L, lua_State *L1, + const char *msg, int level); + +LUALIB_API void (luaL_requiref) (lua_State *L, const char *modname, + lua_CFunction openf, int glb); + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + + +#define luaL_newlibtable(L,l) \ + lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) + +#define luaL_newlib(L,l) \ + (luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) + +#define luaL_argcheck(L, cond,arg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (arg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + +typedef struct luaL_Buffer { + char *b; /* buffer address */ + size_t size; /* buffer size */ + size_t n; /* number of characters in buffer */ + lua_State *L; + char initb[LUAL_BUFFERSIZE]; /* initial buffer */ +} luaL_Buffer; + + +#define luaL_addchar(B,c) \ + ((void)((B)->n < (B)->size || luaL_prepbuffsize((B), 1)), \ + ((B)->b[(B)->n++] = (c))) + +#define luaL_addsize(B,s) ((B)->n += (s)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresultsize) (luaL_Buffer *B, size_t sz); +LUALIB_API char *(luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); + +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) + +/* }====================================================== */ + + + +/* +** {====================================================== +** File handles for IO library +** ======================================================= +*/ + +/* +** A file handle is a userdata with metatable 'LUA_FILEHANDLE' and +** initial structure 'luaL_Stream' (it may contain other fields +** after that initial structure). +*/ + +#define LUA_FILEHANDLE "FILE*" + + +typedef struct luaL_Stream { + FILE *f; /* stream (NULL for incompletely created streams) */ + lua_CFunction closef; /* to close stream (NULL for closed streams) */ +} luaL_Stream; + +/* }====================================================== */ + + + +/* compatibility with old module system */ +#if defined(LUA_COMPAT_MODULE) + +LUALIB_API void (luaL_pushmodule) (lua_State *L, const char *modname, + int sizehint); +LUALIB_API void (luaL_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); + +#define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0)) + +#endif + + +/* +** {================================================================== +** "Abstraction Layer" for basic report of messages and errors +** =================================================================== +*/ + +/* print a string */ +#if !defined(lua_writestring) +#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout) +#endif + +/* print a newline and flush the output */ +#if !defined(lua_writeline) +#define lua_writeline() (lua_writestring("\n", 1), fflush(stdout)) +#endif + +/* print an error message */ +#if !defined(lua_writestringerror) +#define lua_writestringerror(s,p) \ + (fprintf(stderr, (s), (p)), fflush(stderr)) +#endif + +/* }================================================================== */ + + +/* +** {============================================================ +** Compatibility with deprecated conversions +** ============================================================= +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define luaL_checkunsigned(L,a) ((lua_Unsigned)luaL_checkinteger(L,a)) +#define luaL_optunsigned(L,a,d) \ + ((lua_Unsigned)luaL_optinteger(L,a,(lua_Integer)(d))) + +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) + +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#endif +/* }============================================================ */ + + + +#endif + + diff --git a/src/external/lua/include/lua.h b/src/external/lua/include/lua.h new file mode 100644 index 00000000..f78899fc --- /dev/null +++ b/src/external/lua/include/lua.h @@ -0,0 +1,486 @@ +/* +** $Id: lua.h,v 1.331 2016/05/30 15:53:28 roberto Exp $ +** Lua - A Scripting Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION_MAJOR "5" +#define LUA_VERSION_MINOR "3" +#define LUA_VERSION_NUM 503 +#define LUA_VERSION_RELEASE "3" + +#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE +#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2016 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes" + + +/* mark for precompiled code ('Lua') */ +#define LUA_SIGNATURE "\x1bLua" + +/* option for multiple returns in 'lua_pcall' and 'lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** Pseudo-indices +** (-LUAI_MAXSTACK is the minimum valid index; we keep some free empty +** space after that to help overflow detection) +*/ +#define LUA_REGISTRYINDEX (-LUAI_MAXSTACK - 1000) +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) + + +/* thread status */ +#define LUA_OK 0 +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRGCMM 5 +#define LUA_ERRERR 6 + + +typedef struct lua_State lua_State; + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + +#define LUA_NUMTAGS 9 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* predefined values in the registry */ +#define LUA_RIDX_MAINTHREAD 1 +#define LUA_RIDX_GLOBALS 2 +#define LUA_RIDX_LAST LUA_RIDX_GLOBALS + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + +/* unsigned integer type */ +typedef LUA_UNSIGNED lua_Unsigned; + +/* type for continuation-function contexts */ +typedef LUA_KCONTEXT lua_KContext; + + +/* +** Type for C functions registered with Lua +*/ +typedef int (*lua_CFunction) (lua_State *L); + +/* +** Type for continuation functions +*/ +typedef int (*lua_KFunction) (lua_State *L, int status, lua_KContext ctx); + + +/* +** Type for functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); + + +/* +** Type for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* +** RCS ident string +*/ +extern const char lua_ident[]; + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +LUA_API const lua_Number *(lua_version) (lua_State *L); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_absindex) (lua_State *L, int idx); +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_rotate) (lua_State *L, int idx, int n); +LUA_API void (lua_copy) (lua_State *L, int fromidx, int toidx); +LUA_API int (lua_checkstack) (lua_State *L, int n); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isinteger) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum); +LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_rawlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** Comparison and arithmetic functions +*/ + +#define LUA_OPADD 0 /* ORDER TM, ORDER OP */ +#define LUA_OPSUB 1 +#define LUA_OPMUL 2 +#define LUA_OPMOD 3 +#define LUA_OPPOW 4 +#define LUA_OPDIV 5 +#define LUA_OPIDIV 6 +#define LUA_OPBAND 7 +#define LUA_OPBOR 8 +#define LUA_OPBXOR 9 +#define LUA_OPSHL 10 +#define LUA_OPSHR 11 +#define LUA_OPUNM 12 +#define LUA_OPBNOT 13 + +LUA_API void (lua_arith) (lua_State *L, int op); + +#define LUA_OPEQ 0 +#define LUA_OPLT 1 +#define LUA_OPLE 2 + +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_compare) (lua_State *L, int idx1, int idx2, int op); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len); +LUA_API const char *(lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API int (lua_getglobal) (lua_State *L, const char *name); +LUA_API int (lua_gettable) (lua_State *L, int idx); +LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawget) (lua_State *L, int idx); +LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n); +LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p); + +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API int (lua_getuservalue) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_setglobal) (lua_State *L, const char *name); +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n); +LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API void (lua_setuservalue) (lua_State *L, int idx); + + +/* +** 'load' and 'call' functions (load and run Lua code) +*/ +LUA_API void (lua_callk) (lua_State *L, int nargs, int nresults, + lua_KContext ctx, lua_KFunction k); +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) + +LUA_API int (lua_pcallk) (lua_State *L, int nargs, int nresults, int errfunc, + lua_KContext ctx, lua_KFunction k); +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) + +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname, const char *mode); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data, int strip); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yieldk) (lua_State *L, int nresults, lua_KContext ctx, + lua_KFunction k); +LUA_API int (lua_resume) (lua_State *L, lua_State *from, int narg); +LUA_API int (lua_status) (lua_State *L); +LUA_API int (lua_isyieldable) (lua_State *L); + +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) + + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 +#define LUA_GCISRUNNING 9 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); +LUA_API void (lua_len) (lua_State *L, int idx); + +LUA_API size_t (lua_stringtonumber) (lua_State *L, const char *s); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** {============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_getextraspace(L) ((void *)((char *)(L) - LUA_EXTRASPACE)) + +#define lua_tonumber(L,i) lua_tonumberx(L,(i),NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,(i),NULL) + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) lua_pushstring(L, "" s) + +#define lua_pushglobaltable(L) \ + ((void)lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + +#define lua_insert(L,idx) lua_rotate(L, (idx), 1) + +#define lua_remove(L,idx) (lua_rotate(L, (idx), -1), lua_pop(L, 1)) + +#define lua_replace(L,idx) (lua_copy(L, -1, (idx)), lua_pop(L, 1)) + +/* }============================================================== */ + + +/* +** {============================================================== +** compatibility macros for unsigned conversions +** =============================================================== +*/ +#if defined(LUA_COMPAT_APIINTCASTS) + +#define lua_pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) +#define lua_tounsignedx(L,i,is) ((lua_Unsigned)lua_tointegerx(L,i,is)) +#define lua_tounsigned(L,i) lua_tounsignedx(L,(i),NULL) + +#endif +/* }============================================================== */ + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILCALL 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debugger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int (lua_getstack) (lua_State *L, int level, lua_Debug *ar); +LUA_API int (lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *(lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *(lua_getupvalue) (lua_State *L, int funcindex, int n); +LUA_API const char *(lua_setupvalue) (lua_State *L, int funcindex, int n); + +LUA_API void *(lua_upvalueid) (lua_State *L, int fidx, int n); +LUA_API void (lua_upvaluejoin) (lua_State *L, int fidx1, int n1, + int fidx2, int n2); + +LUA_API void (lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook (lua_gethook) (lua_State *L); +LUA_API int (lua_gethookmask) (lua_State *L); +LUA_API int (lua_gethookcount) (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) 'global', 'local', 'field', 'method' */ + const char *what; /* (S) 'Lua', 'C', 'main', 'tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + unsigned char nups; /* (u) number of upvalues */ + unsigned char nparams;/* (u) number of parameters */ + char isvararg; /* (u) */ + char istailcall; /* (t) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + struct CallInfo *i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2016 Lua.org, PUC-Rio. +* +* 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. +******************************************************************************/ + + +#endif diff --git a/src/external/lua/include/lua.hpp b/src/external/lua/include/lua.hpp new file mode 100644 index 00000000..ec417f59 --- /dev/null +++ b/src/external/lua/include/lua.hpp @@ -0,0 +1,9 @@ +// lua.hpp +// Lua header files for C++ +// <> not supplied automatically because Lua also compiles as C++ + +extern "C" { +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" +} diff --git a/src/external/lua/include/luaconf.h b/src/external/lua/include/luaconf.h new file mode 100644 index 00000000..867e9cb1 --- /dev/null +++ b/src/external/lua/include/luaconf.h @@ -0,0 +1,769 @@ +/* +** $Id: luaconf.h,v 1.255 2016/05/01 20:06:09 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef luaconf_h +#define luaconf_h + +#include +#include + + +/* +** =================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +** {==================================================================== +** System Configuration: macros to adapt (if needed) Lua to some +** particular platform, for instance compiling it with 32-bit numbers or +** restricting it to C89. +** ===================================================================== +*/ + +/* +@@ LUA_32BITS enables Lua with 32-bit integers and 32-bit floats. You +** can also define LUA_32BITS in the make file, but changing here you +** ensure that all software connected to Lua will be compiled with the +** same configuration. +*/ +/* #define LUA_32BITS */ + + +/* +@@ LUA_USE_C89 controls the use of non-ISO-C89 features. +** Define it if you want Lua to avoid the use of a few C99 features +** or Windows-specific features on Windows. +*/ +/* #define LUA_USE_C89 */ + + +/* +** By default, Lua on Windows use (some) specific Windows features +*/ +#if !defined(LUA_USE_C89) && defined(_WIN32) && !defined(_WIN32_WCE) +#define LUA_USE_WINDOWS /* enable goodies for regular Windows */ +#endif + + +#if defined(LUA_USE_WINDOWS) +#define LUA_DL_DLL /* enable support for DLL */ +#define LUA_USE_C89 /* broadly, Windows is C89 */ +#endif + + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#endif + + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* MacOS does not need -ldl */ +#define LUA_USE_READLINE /* needs an extra library: -lreadline */ +#endif + + +/* +@@ LUA_C89_NUMBERS ensures that Lua uses the largest types available for +** C89 ('long' and 'double'); Windows always has '__int64', so it does +** not need to use this case. +*/ +#if defined(LUA_USE_C89) && !defined(LUA_USE_WINDOWS) +#define LUA_C89_NUMBERS +#endif + + + +/* +@@ LUAI_BITSINT defines the (minimum) number of bits in an 'int'. +*/ +/* avoid undefined shifts */ +#if ((INT_MAX >> 15) >> 15) >= 1 +#define LUAI_BITSINT 32 +#else +/* 'int' always must have at least 16 bits */ +#define LUAI_BITSINT 16 +#endif + + +/* +@@ LUA_INT_TYPE defines the type for Lua integers. +@@ LUA_FLOAT_TYPE defines the type for Lua floats. +** Lua should work fine with any mix of these options (if supported +** by your C compiler). The usual configurations are 64-bit integers +** and 'double' (the default), 32-bit integers and 'float' (for +** restricted platforms), and 'long'/'double' (for C compilers not +** compliant with C99, which may not have support for 'long long'). +*/ + +/* predefined options for LUA_INT_TYPE */ +#define LUA_INT_INT 1 +#define LUA_INT_LONG 2 +#define LUA_INT_LONGLONG 3 + +/* predefined options for LUA_FLOAT_TYPE */ +#define LUA_FLOAT_FLOAT 1 +#define LUA_FLOAT_DOUBLE 2 +#define LUA_FLOAT_LONGDOUBLE 3 + +#if defined(LUA_32BITS) /* { */ +/* +** 32-bit integers and 'float' +*/ +#if LUAI_BITSINT >= 32 /* use 'int' if big enough */ +#define LUA_INT_TYPE LUA_INT_INT +#else /* otherwise use 'long' */ +#define LUA_INT_TYPE LUA_INT_LONG +#endif +#define LUA_FLOAT_TYPE LUA_FLOAT_FLOAT + +#elif defined(LUA_C89_NUMBERS) /* }{ */ +/* +** largest types available for C89 ('long' and 'double') +*/ +#define LUA_INT_TYPE LUA_INT_LONG +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE + +#endif /* } */ + + +/* +** default configuration for 64-bit Lua ('long long' and 'double') +*/ +#if !defined(LUA_INT_TYPE) +#define LUA_INT_TYPE LUA_INT_LONGLONG +#endif + +#if !defined(LUA_FLOAT_TYPE) +#define LUA_FLOAT_TYPE LUA_FLOAT_DOUBLE +#endif + +/* }================================================================== */ + + + + +/* +** {================================================================== +** Configuration for Paths. +** =================================================================== +*/ + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +** Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +** C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#define LUA_VDIR LUA_VERSION_MAJOR "." LUA_VERSION_MINOR +#if defined(_WIN32) /* { */ +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_SHRDIR "!\\..\\share\\lua\\" LUA_VDIR "\\" +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua;" \ + LUA_SHRDIR"?.lua;" LUA_SHRDIR"?\\init.lua;" \ + ".\\?.lua;" ".\\?\\init.lua" +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.dll;" \ + LUA_CDIR"..\\lib\\lua\\" LUA_VDIR "\\?.dll;" \ + LUA_CDIR"loadall.dll;" ".\\?.dll;" \ + LUA_CDIR"?53.dll;" ".\\?53.dll" + +#else /* }{ */ + +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/" LUA_VDIR "/" +#define LUA_CDIR LUA_ROOT "lib/lua/" LUA_VDIR "/" +#define LUA_PATH_DEFAULT \ + LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua;" \ + "./?.lua;" "./?/init.lua" +#define LUA_CPATH_DEFAULT \ + LUA_CDIR"?.so;" LUA_CDIR"loadall.so;" "./?.so;" \ + LUA_CDIR"lib?53.so;" "./lib?53.so" +#endif /* } */ + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Marks for exported symbols in the C code +** =================================================================== +*/ + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all auxiliary library functions. +@@ LUAMOD_API is a mark for all standard library opening functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) /* { */ + +#if defined(LUA_CORE) || defined(LUA_LIB) /* { */ +#define LUA_API __declspec(dllexport) +#else /* }{ */ +#define LUA_API __declspec(dllimport) +#endif /* } */ + +#else /* }{ */ + +#define LUA_API extern + +#endif /* } */ + + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API +#define LUAMOD_API LUALIB_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +** exported to outside modules. +@@ LUAI_DDEF and LUAI_DDEC are marks for all extern (const) variables +** that are not to be exported to outside modules (LUAI_DDEF for +** definitions and LUAI_DDEC for declarations). +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. Not all elf targets support +** this attribute. Unfortunately, gcc does not offer a way to check +** whether the target offers that support, and those without support +** give a warning about it. To avoid these warnings, change to the +** default definition. +*/ +#if defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) /* { */ +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#else /* }{ */ +#define LUAI_FUNC extern +#endif /* } */ + +#define LUAI_DDEC LUAI_FUNC +#define LUAI_DDEF /* empty */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Compatibility with previous versions +** =================================================================== +*/ + +/* +@@ LUA_COMPAT_5_2 controls other macros for compatibility with Lua 5.2. +@@ LUA_COMPAT_5_1 controls other macros for compatibility with Lua 5.1. +** You can define it to get all options, or change specific options +** to fit your specific needs. +*/ +#if defined(LUA_COMPAT_5_2) /* { */ + +/* +@@ LUA_COMPAT_MATHLIB controls the presence of several deprecated +** functions in the mathematical library. +*/ +#define LUA_COMPAT_MATHLIB + +/* +@@ LUA_COMPAT_BITLIB controls the presence of library 'bit32'. +*/ +#define LUA_COMPAT_BITLIB + +/* +@@ LUA_COMPAT_IPAIRS controls the effectiveness of the __ipairs metamethod. +*/ +#define LUA_COMPAT_IPAIRS + +/* +@@ LUA_COMPAT_APIINTCASTS controls the presence of macros for +** manipulating other integer types (lua_pushunsigned, lua_tounsigned, +** luaL_checkint, luaL_checklong, etc.) +*/ +#define LUA_COMPAT_APIINTCASTS + +#endif /* } */ + + +#if defined(LUA_COMPAT_5_1) /* { */ + +/* Incompatibilities from 5.2 -> 5.3 */ +#define LUA_COMPAT_MATHLIB +#define LUA_COMPAT_APIINTCASTS + +/* +@@ LUA_COMPAT_UNPACK controls the presence of global 'unpack'. +** You can replace it with 'table.unpack'. +*/ +#define LUA_COMPAT_UNPACK + +/* +@@ LUA_COMPAT_LOADERS controls the presence of table 'package.loaders'. +** You can replace it with 'package.searchers'. +*/ +#define LUA_COMPAT_LOADERS + +/* +@@ macro 'lua_cpcall' emulates deprecated function lua_cpcall. +** You can call your C function directly (with light C functions). +*/ +#define lua_cpcall(L,f,u) \ + (lua_pushcfunction(L, (f)), \ + lua_pushlightuserdata(L,(u)), \ + lua_pcall(L,1,0,0)) + + +/* +@@ LUA_COMPAT_LOG10 defines the function 'log10' in the math library. +** You can rewrite 'log10(x)' as 'log(x, 10)'. +*/ +#define LUA_COMPAT_LOG10 + +/* +@@ LUA_COMPAT_LOADSTRING defines the function 'loadstring' in the base +** library. You can rewrite 'loadstring(s)' as 'load(s)'. +*/ +#define LUA_COMPAT_LOADSTRING + +/* +@@ LUA_COMPAT_MAXN defines the function 'maxn' in the table library. +*/ +#define LUA_COMPAT_MAXN + +/* +@@ The following macros supply trivial compatibility for some +** changes in the API. The macros themselves document how to +** change your code to avoid using them. +*/ +#define lua_strlen(L,i) lua_rawlen(L, (i)) + +#define lua_objlen(L,i) lua_rawlen(L, (i)) + +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) + +/* +@@ LUA_COMPAT_MODULE controls compatibility with previous +** module functions 'module' (Lua) and 'luaL_register' (C). +*/ +#define LUA_COMPAT_MODULE + +#endif /* } */ + + +/* +@@ LUA_COMPAT_FLOATSTRING makes Lua format integral floats without a +@@ a float mark ('.0'). +** This macro is not on by default even in compatibility mode, +** because this is not really an incompatibility. +*/ +/* #define LUA_COMPAT_FLOATSTRING */ + +/* }================================================================== */ + + + +/* +** {================================================================== +** Configuration for Numbers. +** Change these definitions if no predefined LUA_FLOAT_* / LUA_INT_* +** satisfy your needs. +** =================================================================== +*/ + +/* +@@ LUA_NUMBER is the floating-point type used by Lua. +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@@ over a floating number. +@@ l_mathlim(x) corrects limit name 'x' to the proper float type +** by prefixing it with one of FLT/DBL/LDBL. +@@ LUA_NUMBER_FRMLEN is the length modifier for writing floats. +@@ LUA_NUMBER_FMT is the format for writing floats. +@@ lua_number2str converts a float to a string. +@@ l_mathop allows the addition of an 'l' or 'f' to all math operations. +@@ l_floor takes the floor of a float. +@@ lua_str2number converts a decimal numeric string to a number. +*/ + + +/* The following definitions are good for most cases here */ + +#define l_floor(x) (l_mathop(floor)(x)) + +#define lua_number2str(s,sz,n) l_sprintf((s), sz, LUA_NUMBER_FMT, (n)) + +/* +@@ lua_numbertointeger converts a float number to an integer, or +** returns 0 if float is not within the range of a lua_Integer. +** (The range comparisons are tricky because of rounding. The tests +** here assume a two-complement representation, where MININTEGER always +** has an exact representation as a float; MAXINTEGER may not have one, +** and therefore its conversion to float may have an ill-defined value.) +*/ +#define lua_numbertointeger(n,p) \ + ((n) >= (LUA_NUMBER)(LUA_MININTEGER) && \ + (n) < -(LUA_NUMBER)(LUA_MININTEGER) && \ + (*(p) = (LUA_INTEGER)(n), 1)) + + +/* now the variable definitions */ + +#if LUA_FLOAT_TYPE == LUA_FLOAT_FLOAT /* { single float */ + +#define LUA_NUMBER float + +#define l_mathlim(n) (FLT_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.7g" + +#define l_mathop(op) op##f + +#define lua_str2number(s,p) strtof((s), (p)) + + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE /* }{ long double */ + +#define LUA_NUMBER long double + +#define l_mathlim(n) (LDBL_##n) + +#define LUAI_UACNUMBER long double + +#define LUA_NUMBER_FRMLEN "L" +#define LUA_NUMBER_FMT "%.19Lg" + +#define l_mathop(op) op##l + +#define lua_str2number(s,p) strtold((s), (p)) + +#elif LUA_FLOAT_TYPE == LUA_FLOAT_DOUBLE /* }{ double */ + +#define LUA_NUMBER double + +#define l_mathlim(n) (DBL_##n) + +#define LUAI_UACNUMBER double + +#define LUA_NUMBER_FRMLEN "" +#define LUA_NUMBER_FMT "%.14g" + +#define l_mathop(op) op + +#define lua_str2number(s,p) strtod((s), (p)) + +#else /* }{ */ + +#error "numeric float type not defined" + +#endif /* } */ + + + +/* +@@ LUA_INTEGER is the integer type used by Lua. +** +@@ LUA_UNSIGNED is the unsigned version of LUA_INTEGER. +** +@@ LUAI_UACINT is the result of an 'usual argument conversion' +@@ over a lUA_INTEGER. +@@ LUA_INTEGER_FRMLEN is the length modifier for reading/writing integers. +@@ LUA_INTEGER_FMT is the format for writing integers. +@@ LUA_MAXINTEGER is the maximum value for a LUA_INTEGER. +@@ LUA_MININTEGER is the minimum value for a LUA_INTEGER. +@@ lua_integer2str converts an integer to a string. +*/ + + +/* The following definitions are good for most cases here */ + +#define LUA_INTEGER_FMT "%" LUA_INTEGER_FRMLEN "d" +#define lua_integer2str(s,sz,n) l_sprintf((s), sz, LUA_INTEGER_FMT, (n)) + +#define LUAI_UACINT LUA_INTEGER + +/* +** use LUAI_UACINT here to avoid problems with promotions (which +** can turn a comparison between unsigneds into a signed comparison) +*/ +#define LUA_UNSIGNED unsigned LUAI_UACINT + + +/* now the variable definitions */ + +#if LUA_INT_TYPE == LUA_INT_INT /* { int */ + +#define LUA_INTEGER int +#define LUA_INTEGER_FRMLEN "" + +#define LUA_MAXINTEGER INT_MAX +#define LUA_MININTEGER INT_MIN + +#elif LUA_INT_TYPE == LUA_INT_LONG /* }{ long */ + +#define LUA_INTEGER long +#define LUA_INTEGER_FRMLEN "l" + +#define LUA_MAXINTEGER LONG_MAX +#define LUA_MININTEGER LONG_MIN + +#elif LUA_INT_TYPE == LUA_INT_LONGLONG /* }{ long long */ + +/* use presence of macro LLONG_MAX as proxy for C99 compliance */ +#if defined(LLONG_MAX) /* { */ +/* use ISO C99 stuff */ + +#define LUA_INTEGER long long +#define LUA_INTEGER_FRMLEN "ll" + +#define LUA_MAXINTEGER LLONG_MAX +#define LUA_MININTEGER LLONG_MIN + +#elif defined(LUA_USE_WINDOWS) /* }{ */ +/* in Windows, can use specific Windows types */ + +#define LUA_INTEGER __int64 +#define LUA_INTEGER_FRMLEN "I64" + +#define LUA_MAXINTEGER _I64_MAX +#define LUA_MININTEGER _I64_MIN + +#else /* }{ */ + +#error "Compiler does not support 'long long'. Use option '-DLUA_32BITS' \ + or '-DLUA_C89_NUMBERS' (see file 'luaconf.h' for details)" + +#endif /* } */ + +#else /* }{ */ + +#error "numeric integer type not defined" + +#endif /* } */ + +/* }================================================================== */ + + +/* +** {================================================================== +** Dependencies with C99 and other C details +** =================================================================== +*/ + +/* +@@ l_sprintf is equivalent to 'snprintf' or 'sprintf' in C89. +** (All uses in Lua have only one format item.) +*/ +#if !defined(LUA_USE_C89) +#define l_sprintf(s,sz,f,i) snprintf(s,sz,f,i) +#else +#define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s,f,i)) +#endif + + +/* +@@ lua_strx2number converts an hexadecimal numeric string to a number. +** In C99, 'strtod' does that conversion. Otherwise, you can +** leave 'lua_strx2number' undefined and Lua will provide its own +** implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_strx2number(s,p) lua_str2number(s,p) +#endif + + +/* +@@ lua_number2strx converts a float to an hexadecimal numeric string. +** In C99, 'sprintf' (with format specifiers '%a'/'%A') does that. +** Otherwise, you can leave 'lua_number2strx' undefined and Lua will +** provide its own implementation. +*/ +#if !defined(LUA_USE_C89) +#define lua_number2strx(L,b,sz,f,n) ((void)L, l_sprintf(b,sz,f,n)) +#endif + + +/* +** 'strtof' and 'opf' variants for math functions are not valid in +** C89. Otherwise, the macro 'HUGE_VALF' is a good proxy for testing the +** availability of these variants. ('math.h' is already included in +** all files that use these macros.) +*/ +#if defined(LUA_USE_C89) || (defined(HUGE_VAL) && !defined(HUGE_VALF)) +#undef l_mathop /* variants not available */ +#undef lua_str2number +#define l_mathop(op) (lua_Number)op /* no variant */ +#define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) +#endif + + +/* +@@ LUA_KCONTEXT is the type of the context ('ctx') for continuation +** functions. It must be a numerical type; Lua will use 'intptr_t' if +** available, otherwise it will use 'ptrdiff_t' (the nearest thing to +** 'intptr_t' in C89) +*/ +#define LUA_KCONTEXT ptrdiff_t + +#if !defined(LUA_USE_C89) && defined(__STDC_VERSION__) && \ + __STDC_VERSION__ >= 199901L +#include +#if defined(INTPTR_MAX) /* even in C99 this type is optional */ +#undef LUA_KCONTEXT +#define LUA_KCONTEXT intptr_t +#endif +#endif + + +/* +@@ lua_getlocaledecpoint gets the locale "radix character" (decimal point). +** Change that if you do not want to use C locales. (Code using this +** macro must include header 'locale.h'.) +*/ +#if !defined(lua_getlocaledecpoint) +#define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Language Variations +** ===================================================================== +*/ + +/* +@@ LUA_NOCVTN2S/LUA_NOCVTS2N control how Lua performs some +** coercions. Define LUA_NOCVTN2S to turn off automatic coercion from +** numbers to strings. Define LUA_NOCVTS2N to turn off automatic +** coercion from strings to numbers. +*/ +/* #define LUA_NOCVTN2S */ +/* #define LUA_NOCVTS2N */ + + +/* +@@ LUA_USE_APICHECK turns on several consistency checks on the C API. +** Define it as a help when debugging C code. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(l,e) assert(e) +#endif + +/* }================================================================== */ + + +/* +** {================================================================== +** Macros that affect the API and must be stable (that is, must be the +** same when you compile Lua and when you compile code that links to +** Lua). You probably do not want/need to change them. +** ===================================================================== +*/ + +/* +@@ LUAI_MAXSTACK limits the size of the Lua stack. +** CHANGE it if you need a different limit. This limit is arbitrary; +** its only purpose is to stop Lua from consuming unlimited stack +** space (and to reserve some numbers for pseudo-indices). +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_MAXSTACK 1000000 +#else +#define LUAI_MAXSTACK 15000 +#endif + + +/* +@@ LUA_EXTRASPACE defines the size of a raw memory area associated with +** a Lua state with very fast access. +** CHANGE it if you need a different size. +*/ +#define LUA_EXTRASPACE (sizeof(void *)) + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@@ of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +** CHANGE it if it uses too much C-stack space. (For long double, +** 'string.format("%.99f", 1e4932)' needs ~5030 bytes, so a +** smaller buffer would force a memory allocation for each call to +** 'string.format'.) +*/ +#if defined(LUA_FLOAT_LONGDOUBLE) +#define LUAL_BUFFERSIZE 8192 +#else +#define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer))) +#endif + +/* }================================================================== */ + + +/* +@@ LUA_QL describes how error messages quote program elements. +** Lua does not use these macros anymore; they are here for +** compatibility only. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + + + +#endif + diff --git a/src/external/lua/include/lualib.h b/src/external/lua/include/lualib.h new file mode 100644 index 00000000..5165c0fb --- /dev/null +++ b/src/external/lua/include/lualib.h @@ -0,0 +1,58 @@ +/* +** $Id: lualib.h,v 1.44 2014/02/06 17:32:33 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + + +LUAMOD_API int (luaopen_base) (lua_State *L); + +#define LUA_COLIBNAME "coroutine" +LUAMOD_API int (luaopen_coroutine) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUAMOD_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUAMOD_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUAMOD_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUAMOD_API int (luaopen_string) (lua_State *L); + +#define LUA_UTF8LIBNAME "utf8" +LUAMOD_API int (luaopen_utf8) (lua_State *L); + +#define LUA_BITLIBNAME "bit32" +LUAMOD_API int (luaopen_bit32) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUAMOD_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUAMOD_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUAMOD_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#if !defined(lua_assert) +#define lua_assert(x) ((void)0) +#endif + + +#endif diff --git a/src/external/lua/lib/liblua53.a b/src/external/lua/lib/liblua53.a new file mode 100644 index 00000000..e51c0c80 Binary files /dev/null and b/src/external/lua/lib/liblua53.a differ diff --git a/src/external/lua/lib/liblua53dll.a b/src/external/lua/lib/liblua53dll.a new file mode 100644 index 00000000..32646db3 Binary files /dev/null and b/src/external/lua/lib/liblua53dll.a differ diff --git a/src/rlua.h b/src/rlua.h new file mode 100644 index 00000000..6a909c43 --- /dev/null +++ b/src/rlua.h @@ -0,0 +1,3631 @@ +/********************************************************************************************** +* +* rlua - raylib Lua bindings +* +* NOTE 01: +* The following types: +* Color, Vector2, Vector3, Rectangle, Ray, Camera +* are treated as objects with named fields, same as in C. +* +* Lua defines utility functions for creating those objects. +* Usage: +* local cl = Color(255,255,255,255) +* local rec = Rectangle(10, 10, 100, 100) +* local ray = Ray(Vector3(20, 20, 20), Vector3(50, 50, 50)) +* local x2 = rec.x + rec.width +* +* The following types: +* Image, Texture2D, SpriteFont +* are immutable, and you can only read their non-pointer arguments (e.g. sprfnt.size). +* +* All other object types are opaque, that is, you cannot access or +* change their fields directly. +* +* Remember that ALL raylib types have REFERENCE SEMANTICS in Lua. +* There is currently no way to create a copy of an opaque object. +* +* NOTE 02: +* Some raylib functions take a pointer to an array, and the size of that array. +* The equivalent Lua functions take only an array table of the specified type UNLESS +* it's a pointer to a large char array (e.g. for images), then it takes (and potentially returns) +* a Lua string (without the size argument, as Lua strings are sized by default). +* +* NOTE 03: +* Some raylib functions take pointers to objects to modify (e.g. ImageToPOT, etc.) +* In Lua, these functions take values and return a new changed value, instead. +* So, in C: +* ImageToPOT(&img, BLACK); +* In Lua becomes: +* img = ImageToPOT(img, BLACK) +* +* Remember that functions can return multiple values, so: +* UpdateCameraPlayer(&cam, &playerPos); +* Vector3 vec = ResolveCollisionCubicmap(img, mapPos, &playerPos, 5.0); +* becomes: +* cam, playerPos = UpdateCameraPlayer(cam, playerPos) +* vec, playerPos = ResolveCollisionCubicmap(img, mapPos, playerPos, 5) +* +* This is to preserve value semantics of raylib objects. +* +* +* This Lua binding for raylib was originally created by Ghassan Al-Mashareqa (ghassan@ghassan.pl) +* for raylib 1.3 and later on reviewed and updated to raylib 1.6 by Ramon Santamaria. +* +* Copyright (c) 2015-2016 Ghassan Al-Mashareqa and Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#pragma once + +#include "raylib.h" + +#define RLUA_STATIC +#ifdef RLUA_STATIC + #define RLUADEF static // Functions just visible to module including this file +#else + #ifdef __cplusplus + #define RLUADEF extern "C" // Functions visible from other files (no name mangling of functions in C++) + #else + #define RLUADEF extern // Functions visible from other files + #endif +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +RLUADEF void InitLuaDevice(void); // Initialize Lua system +RLUADEF void ExecuteLuaCode(const char *code); // Execute raylib Lua code +RLUADEF void ExecuteLuaFile(const char *filename); // Execute raylib Lua script +RLUADEF void CloseLuaDevice(void); // De-initialize Lua system + +/*********************************************************************************** +* +* RLUA IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RLUA_IMPLEMENTATION) + +#include "raylib.h" +#include "utils.h" +#include "raymath.h" + +#include +#include + +#include +#include +#include + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define LuaPush_Image(L, img) LuaPushOpaqueTypeWithMetatable(L, img, Image) +#define LuaPush_Texture2D(L, tex) LuaPushOpaqueTypeWithMetatable(L, tex, Texture2D) +#define LuaPush_RenderTexture2D(L, tex) LuaPushOpaqueTypeWithMetatable(L, tex, RenderTexture2D) +#define LuaPush_SpriteFont(L, sf) LuaPushOpaqueTypeWithMetatable(L, sf, SpriteFont) +#define LuaPush_Mesh(L, vd) LuaPushOpaqueType(L, vd) +#define LuaPush_Shader(L, s) LuaPushOpaqueType(L, s) +#define LuaPush_Sound(L, snd) LuaPushOpaqueType(L, snd) +#define LuaPush_Wave(L, wav) LuaPushOpaqueType(L, wav) +#define LuaPush_Music(L, mus) LuaPushOpaqueType(L, mus) + +#define LuaGetArgument_string luaL_checkstring +#define LuaGetArgument_int (int)luaL_checkinteger +#define LuaGetArgument_unsigned (unsigned)luaL_checkinteger +#define LuaGetArgument_char (char)luaL_checkinteger +#define LuaGetArgument_float (float)luaL_checknumber +#define LuaGetArgument_double luaL_checknumber +#define LuaGetArgument_Image(L, img) *(Image*)LuaGetArgumentOpaqueTypeWithMetatable(L, img, "Image") +#define LuaGetArgument_Texture2D(L, tex) *(Texture2D*)LuaGetArgumentOpaqueTypeWithMetatable(L, tex, "Texture2D") +#define LuaGetArgument_RenderTexture2D(L, rtex) *(RenderTexture2D*)LuaGetArgumentOpaqueTypeWithMetatable(L, rtex, "RenderTexture2D") +#define LuaGetArgument_SpriteFont(L, sf) *(SpriteFont*)LuaGetArgumentOpaqueTypeWithMetatable(L, sf, "SpriteFont") +#define LuaGetArgument_Mesh(L, vd) *(Mesh*)LuaGetArgumentOpaqueType(L, vd) +#define LuaGetArgument_Shader(L, s) *(Shader*)LuaGetArgumentOpaqueType(L, s) +#define LuaGetArgument_Sound(L, snd) *(Sound*)LuaGetArgumentOpaqueType(L, snd) +#define LuaGetArgument_Wave(L, wav) *(Wave*)LuaGetArgumentOpaqueType(L, wav) +#define LuaGetArgument_Music(L, mus) *(Music*)LuaGetArgumentOpaqueType(L, mus) + +#define LuaPushOpaqueType(L, str) LuaPushOpaque(L, &str, sizeof(str)) +#define LuaPushOpaqueTypeWithMetatable(L, str, meta) LuaPushOpaqueWithMetatable(L, &str, sizeof(str), #meta) + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static lua_State* mainLuaState = 0; +static lua_State* L = 0; + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static void LuaPush_Color(lua_State* L, Color color); +static void LuaPush_Vector2(lua_State* L, Vector2 vec); +static void LuaPush_Vector3(lua_State* L, Vector3 vec); +static void LuaPush_Quaternion(lua_State* L, Quaternion vec); +static void LuaPush_Matrix(lua_State* L, Matrix *matrix); +static void LuaPush_Rectangle(lua_State* L, Rectangle rect); +static void LuaPush_Model(lua_State* L, Model mdl); +static void LuaPush_Ray(lua_State* L, Ray ray); +static void LuaPush_Camera(lua_State* L, Camera cam); + +static Vector2 LuaGetArgument_Vector2(lua_State* L, int index); +static Vector3 LuaGetArgument_Vector3(lua_State* L, int index); +static Quaternion LuaGetArgument_Quaternion(lua_State* L, int index); +static Color LuaGetArgument_Color(lua_State* L, int index); +static Rectangle LuaGetArgument_Rectangle(lua_State* L, int index); +static Camera LuaGetArgument_Camera(lua_State* L, int index); +static Ray LuaGetArgument_Ray(lua_State* L, int index); +static Matrix LuaGetArgument_Matrix(lua_State* L, int index); +static Model LuaGetArgument_Model(lua_State* L, int index); + +//---------------------------------------------------------------------------------- +// rlua Helper Functions +//---------------------------------------------------------------------------------- +static void LuaStartEnum(void) +{ + lua_newtable(L); +} + +static void LuaSetEnum(const char *name, int value) +{ + lua_pushinteger(L, value); + lua_setfield(L, -2, name); +} + +static void LuaSetEnumColor(const char *name, Color color) +{ + LuaPush_Color(L, color); + lua_setfield(L, -2, name); +} + +static void LuaEndEnum(const char *name) +{ + lua_setglobal(L, name); +} + +static void LuaPushOpaque(lua_State* L, void *ptr, size_t size) +{ + void *ud = lua_newuserdata(L, size); + memcpy(ud, ptr, size); +} + +static void LuaPushOpaqueWithMetatable(lua_State* L, void *ptr, size_t size, const char *metatable_name) +{ + void *ud = lua_newuserdata(L, size); + memcpy(ud, ptr, size); + luaL_setmetatable(L, metatable_name); +} + +static void* LuaGetArgumentOpaqueType(lua_State* L, int index) +{ + return lua_touserdata(L, index); +} + +static void* LuaGetArgumentOpaqueTypeWithMetatable(lua_State* L, int index, const char *metatable_name) +{ + return luaL_checkudata(L, index, metatable_name); +} + +//---------------------------------------------------------------------------------- +// LuaIndex* functions +//---------------------------------------------------------------------------------- +static int LuaIndexImage(lua_State* L) +{ + Image img = LuaGetArgument_Image(L, 1); + const char *key = luaL_checkstring(L, 2); + if (!strcmp(key, "width")) + lua_pushinteger(L, img.width); + else if (!strcmp(key, "height")) + lua_pushinteger(L, img.height); + else if (!strcmp(key, "mipmaps")) + lua_pushinteger(L, img.mipmaps); + else if (!strcmp(key, "format")) + lua_pushinteger(L, img.format); + else + return 0; + return 1; +} + +static int LuaIndexTexture2D(lua_State* L) +{ + Texture2D img = LuaGetArgument_Texture2D(L, 1); + const char *key = luaL_checkstring(L, 2); + if (!strcmp(key, "width")) + lua_pushinteger(L, img.width); + else if (!strcmp(key, "height")) + lua_pushinteger(L, img.height); + else if (!strcmp(key, "mipmaps")) + lua_pushinteger(L, img.mipmaps); + else if (!strcmp(key, "format")) + lua_pushinteger(L, img.format); + else + return 0; + return 1; +} + +// TODO: RenderTexture2D? + +static int LuaIndexSpriteFont(lua_State* L) +{ + SpriteFont img = LuaGetArgument_SpriteFont(L, 1); + const char *key = luaL_checkstring(L, 2); + if (!strcmp(key, "size")) + lua_pushinteger(L, img.size); + else if (!strcmp(key, "texture")) + LuaPush_Texture2D(L, img.texture); + else if (!strcmp(key, "numChars")) + lua_pushinteger(L, img.numChars); + else + return 0; + return 1; +} + +static void LuaBuildOpaqueMetatables(void) +{ + luaL_newmetatable(L, "Image"); + lua_pushcfunction(L, &LuaIndexImage); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + luaL_newmetatable(L, "Texture2D"); + lua_pushcfunction(L, &LuaIndexTexture2D); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + // TODO? + /* + luaL_newmetatable(L, "RenderTexture2D"); + lua_pushcfunction(L, &LuaIndexRenderTexture2D); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + */ + luaL_newmetatable(L, "SpriteFont"); + lua_pushcfunction(L, &LuaIndexSpriteFont); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +} + +//---------------------------------------------------------------------------------- +// LuaGetArgument functions +//---------------------------------------------------------------------------------- + +static Vector2 LuaGetArgument_Vector2(lua_State* L, int index) +{ + luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Vector2"); + float x = (float)lua_tonumber(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Vector2"); + float y = (float)lua_tonumber(L, -1); + lua_pop(L, 2); + return (Vector2) { x, y }; +} + +static Vector3 LuaGetArgument_Vector3(lua_State* L, int index) +{ + luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Vector3"); + float x = (float)lua_tonumber(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Vector3"); + float y = (float)lua_tonumber(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "z") == LUA_TNUMBER, index, "Expected Vector3"); + float z = (float)lua_tonumber(L, -1); + lua_pop(L, 3); + return (Vector3) { x, y, z }; +} + +static Quaternion LuaGetArgument_Quaternion(lua_State* L, int index) +{ + luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Quaternion"); + float x = (float)lua_tonumber(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Quaternion"); + float y = (float)lua_tonumber(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "z") == LUA_TNUMBER, index, "Expected Quaternion"); + float z = (float)lua_tonumber(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "w") == LUA_TNUMBER, index, "Expected Quaternion"); + float w = (float)lua_tonumber(L, -1); + lua_pop(L, 4); + return (Quaternion) { x, y, z, w }; +} + +static Color LuaGetArgument_Color(lua_State* L, int index) +{ + luaL_argcheck(L, lua_getfield(L, index, "r") == LUA_TNUMBER, index, "Expected Color"); + unsigned char r = (unsigned char)lua_tointeger(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "g") == LUA_TNUMBER, index, "Expected Color"); + unsigned char g = (unsigned char)lua_tointeger(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "b") == LUA_TNUMBER, index, "Expected Color"); + unsigned char b = (unsigned char)lua_tointeger(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "a") == LUA_TNUMBER, index, "Expected Color"); + unsigned char a = (unsigned char)lua_tointeger(L, -1); + lua_pop(L, 4); + return (Color) { r, g, b, a }; +} + +static Rectangle LuaGetArgument_Rectangle(lua_State* L, int index) +{ + luaL_argcheck(L, lua_getfield(L, index, "x") == LUA_TNUMBER, index, "Expected Rectangle"); + int x = (int)lua_tointeger(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "y") == LUA_TNUMBER, index, "Expected Rectangle"); + int y = (int)lua_tointeger(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "width") == LUA_TNUMBER, index, "Expected Rectangle"); + int w = (int)lua_tointeger(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "height") == LUA_TNUMBER, index, "Expected Rectangle"); + int h = (int)lua_tointeger(L, -1); + lua_pop(L, 4); + return (Rectangle) { x, y, w, h }; +} + +static Camera LuaGetArgument_Camera(lua_State* L, int index) +{ + Camera result; + luaL_argcheck(L, lua_getfield(L, index, "position") == LUA_TTABLE, index, "Expected Camera"); + result.position = LuaGetArgument_Vector3(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "target") == LUA_TTABLE, index, "Expected Camera"); + result.target = LuaGetArgument_Vector3(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "up") == LUA_TTABLE, index, "Expected Camera"); + result.up = LuaGetArgument_Vector3(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "fovy") == LUA_TTABLE, index, "Expected Camera"); + result.fovy = LuaGetArgument_float(L, -1); + lua_pop(L, 4); + return result; +} + +static Camera2D LuaGetArgument_Camera2D(lua_State* L, int index) +{ + Camera2D result; + luaL_argcheck(L, lua_getfield(L, index, "offset") == LUA_TTABLE, index, "Expected Camera2D"); + result.offset = LuaGetArgument_Vector2(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "target") == LUA_TTABLE, index, "Expected Camera2D"); + result.target = LuaGetArgument_Vector2(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "rotation") == LUA_TTABLE, index, "Expected Camera2D"); + result.rotation = LuaGetArgument_float(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "zoom") == LUA_TTABLE, index, "Expected Camera2D"); + result.zoom = LuaGetArgument_float(L, -1); + lua_pop(L, 4); + return result; +} + +// TODO: +//BoundingBox +//LightData, *Light +//MusicData *Music; +//AudioStream + +// TODO: Review Mesh, Shader + +static BoundingBox LuaGetArgument_BoundingBox(lua_State* L, int index) +{ + BoundingBox result; + luaL_argcheck(L, lua_getfield(L, index, "min") == LUA_TTABLE, index, "Expected BoundingBox"); + result.min = LuaGetArgument_Vector3(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "max") == LUA_TTABLE, index, "Expected BoundingBox"); + result.max = LuaGetArgument_Vector3(L, -1); + lua_pop(L, 2); + return result; +} + +static Ray LuaGetArgument_Ray(lua_State* L, int index) +{ + Ray result; + luaL_argcheck(L, lua_getfield(L, index, "position") == LUA_TTABLE, index, "Expected Ray"); + result.position = LuaGetArgument_Vector3(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "direction") == LUA_TTABLE, index, "Expected Ray"); + result.direction = LuaGetArgument_Vector3(L, -1); + lua_pop(L, 2); + return result; +} + +static Matrix LuaGetArgument_Matrix(lua_State* L, int index) +{ + Matrix result = { 0 }; + float* ptr = &result.m0; + for (int i = 0; i < 16; i++) + { + lua_geti(L, -1, i+1); + ptr[i] = luaL_checkinteger(L, -1); + } + lua_pop(L, 16); + return result; +} + +static Material LuaGetArgument_Material(lua_State* L, int index) +{ + Material result; + luaL_argcheck(L, lua_getfield(L, index, "shader") == LUA_TUSERDATA, index, "Expected Material"); + result.shader = LuaGetArgument_Shader(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "texDiffuse") == LUA_TUSERDATA, index, "Expected Material"); + result.texDiffuse = LuaGetArgument_Texture2D(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "texNormal") == LUA_TUSERDATA, index, "Expected Material"); + result.texNormal = LuaGetArgument_Texture2D(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "texSpecular") == LUA_TUSERDATA, index, "Expected Material"); + result.texSpecular = LuaGetArgument_Texture2D(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "colDiffuse") == LUA_TUSERDATA, index, "Expected Material"); + result.colDiffuse = LuaGetArgument_Color(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "colAmbient") == LUA_TUSERDATA, index, "Expected Material"); + result.colAmbient = LuaGetArgument_Color(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "colSpecular") == LUA_TUSERDATA, index, "Expected Material"); + result.colSpecular = LuaGetArgument_Color(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "glossiness") == LUA_TUSERDATA, index, "Expected Material"); + result.glossiness = LuaGetArgument_float(L, -1); + lua_pop(L, 8); + return result; +} + +static Model LuaGetArgument_Model(lua_State* L, int index) +{ + Model result; + luaL_argcheck(L, lua_getfield(L, index, "mesh") == LUA_TUSERDATA, index, "Expected Model"); + result.mesh = LuaGetArgument_Mesh(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "transform") == LUA_TTABLE, index, "Expected Model"); + result.transform = LuaGetArgument_Matrix(L, -1); + luaL_argcheck(L, lua_getfield(L, index, "material") == LUA_TTABLE, index, "Expected Model"); + result.material = LuaGetArgument_Material(L, -1); + lua_pop(L, 3); + return result; +} + +//---------------------------------------------------------------------------------- +// LuaPush functions +//---------------------------------------------------------------------------------- +static void LuaPush_Color(lua_State* L, Color color) +{ + lua_createtable(L, 0, 4); + lua_pushinteger(L, color.r); + lua_setfield(L, -2, "r"); + lua_pushinteger(L, color.g); + lua_setfield(L, -2, "g"); + lua_pushinteger(L, color.b); + lua_setfield(L, -2, "b"); + lua_pushinteger(L, color.a); + lua_setfield(L, -2, "a"); +} + +static void LuaPush_Vector2(lua_State* L, Vector2 vec) +{ + lua_createtable(L, 0, 2); + lua_pushnumber(L, vec.x); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, vec.y); + lua_setfield(L, -2, "y"); +} + +static void LuaPush_Vector3(lua_State* L, Vector3 vec) +{ + lua_createtable(L, 0, 3); + lua_pushnumber(L, vec.x); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, vec.y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, vec.z); + lua_setfield(L, -2, "z"); +} + +static void LuaPush_Quaternion(lua_State* L, Quaternion vec) +{ + lua_createtable(L, 0, 4); + lua_pushnumber(L, vec.x); + lua_setfield(L, -2, "x"); + lua_pushnumber(L, vec.y); + lua_setfield(L, -2, "y"); + lua_pushnumber(L, vec.z); + lua_setfield(L, -2, "z"); + lua_pushnumber(L, vec.w); + lua_setfield(L, -2, "w"); +} + +static void LuaPush_Matrix(lua_State* L, Matrix *matrix) +{ + int i; + lua_createtable(L, 16, 0); + float* num = (&matrix->m0); + for (i = 0; i < 16; i++) + { + lua_pushnumber(L, num[i]); + lua_rawseti(L, -2, i + 1); + } +} + +static void LuaPush_Rectangle(lua_State* L, Rectangle rect) +{ + lua_createtable(L, 0, 4); + lua_pushinteger(L, rect.x); + lua_setfield(L, -2, "x"); + lua_pushinteger(L, rect.y); + lua_setfield(L, -2, "y"); + lua_pushinteger(L, rect.width); + lua_setfield(L, -2, "width"); + lua_pushinteger(L, rect.height); + lua_setfield(L, -2, "height"); +} + +static void LuaPush_Ray(lua_State* L, Ray ray) +{ + lua_createtable(L, 0, 2); + LuaPush_Vector3(L, ray.position); + lua_setfield(L, -2, "position"); + LuaPush_Vector3(L, ray.direction); + lua_setfield(L, -2, "direction"); +} + +static void LuaPush_BoundingBox(lua_State* L, BoundingBox bb) +{ + lua_createtable(L, 0, 2); + LuaPush_Vector3(L, bb.min); + lua_setfield(L, -2, "min"); + LuaPush_Vector3(L, bb.max); + lua_setfield(L, -2, "max"); +} + +static void LuaPush_Camera(lua_State* L, Camera cam) +{ + lua_createtable(L, 0, 4); + LuaPush_Vector3(L, cam.position); + lua_setfield(L, -2, "position"); + LuaPush_Vector3(L, cam.target); + lua_setfield(L, -2, "target"); + LuaPush_Vector3(L, cam.up); + lua_setfield(L, -2, "up"); + lua_pushnumber(L, cam.fovy); + lua_setfield(L, -2, "fovy"); +} + +static void LuaPush_Camera2D(lua_State* L, Camera2D cam) +{ + lua_createtable(L, 0, 3); + LuaPush_Vector2(L, cam.offset); + lua_setfield(L, -2, "offset"); + LuaPush_Vector2(L, cam.target); + lua_setfield(L, -2, "target"); + lua_pushnumber(L, cam.rotation); + lua_setfield(L, -2, "rotation"); + lua_pushnumber(L, cam.zoom); + lua_setfield(L, -2, "zoom"); +} + +static void LuaPush_Material(lua_State* L, Material mat) +{ + lua_createtable(L, 0, 8); + LuaPush_Shader(L, mat.shader); + lua_setfield(L, -2, "shader"); + LuaPush_Texture2D(L, mat.texDiffuse); + lua_setfield(L, -2, "texDiffuse"); + LuaPush_Texture2D(L, mat.texNormal); + lua_setfield(L, -2, "texNormal"); + LuaPush_Texture2D(L, mat.texSpecular); + lua_setfield(L, -2, "texSpecular"); + LuaPush_Color(L, mat.colDiffuse); + lua_setfield(L, -2, "colDiffuse"); + LuaPush_Color(L, mat.colAmbient); + lua_setfield(L, -2, "colAmbient"); + LuaPush_Color(L, mat.colSpecular); + lua_setfield(L, -2, "colSpecular"); + lua_pushnumber(L, mat.glossiness); + lua_setfield(L, -2, "glossiness"); +} + +static void LuaPush_Model(lua_State* L, Model mdl) +{ + lua_createtable(L, 0, 4); + LuaPush_Mesh(L, mdl.mesh); + lua_setfield(L, -2, "mesh"); + LuaPush_Matrix(L, &mdl.transform); + lua_setfield(L, -2, "transform"); + LuaPush_Material(L, mdl.material); + lua_setfield(L, -2, "material"); +} + +//---------------------------------------------------------------------------------- +// raylib Lua Structure constructors +//---------------------------------------------------------------------------------- +static int lua_Color(lua_State* L) +{ + LuaPush_Color(L, (Color) { (unsigned char)luaL_checkinteger(L, 1), (unsigned char)luaL_checkinteger(L, 2), (unsigned char)luaL_checkinteger(L, 3), (unsigned char)luaL_checkinteger(L, 4) }); + return 1; +} + +static int lua_Vector2(lua_State* L) +{ + LuaPush_Vector2(L, (Vector2) { (float)luaL_checknumber(L, 1), (float)luaL_checknumber(L, 2) }); + return 1; +} + +static int lua_Vector3(lua_State* L) +{ + LuaPush_Vector3(L, (Vector3) { (float)luaL_checknumber(L, 1), (float)luaL_checknumber(L, 2), (float)luaL_checknumber(L, 3) }); + return 1; +} + +static int lua_Rectangle(lua_State* L) +{ + LuaPush_Rectangle(L, (Rectangle) { (int)luaL_checkinteger(L, 1), (int)luaL_checkinteger(L, 2), (int)luaL_checkinteger(L, 3), (int)luaL_checkinteger(L, 4) }); + return 1; +} + +static int lua_Ray(lua_State* L) +{ + Vector2 pos = LuaGetArgument_Vector2(L, 1); + Vector2 dir = LuaGetArgument_Vector2(L, 2); + LuaPush_Ray(L, (Ray) { { pos.x, pos.y }, { dir.x, dir.y } }); + return 1; +} + +static int lua_Camera(lua_State* L) +{ + Vector3 pos = LuaGetArgument_Vector3(L, 1); + Vector3 tar = LuaGetArgument_Vector3(L, 2); + Vector3 up = LuaGetArgument_Vector3(L, 3); + float fovy = LuaGetArgument_float(L, 4); + LuaPush_Camera(L, (Camera) { { pos.x, pos.y, pos.z }, { tar.x, tar.y, tar.z }, { up.x, up.y, up.z }, fovy }); + return 1; +} + +static int lua_Camera2D(lua_State* L) +{ + Vector2 off = LuaGetArgument_Vector2(L, 1); + Vector2 tar = LuaGetArgument_Vector2(L, 2); + float rot = LuaGetArgument_float(L, 3); + float zoom = LuaGetArgument_float(L, 4); + LuaPush_Camera2D(L, (Camera2D) { { off.x, off.y }, { tar.x, tar.y }, rot, zoom }); + return 1; +} + + +/************************************************************************************* +* raylib Lua Functions Bindings +**************************************************************************************/ + +//------------------------------------------------------------------------------------ +// raylib [core] module functions - Window and Graphics Device +//------------------------------------------------------------------------------------ +int lua_InitWindow(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + const char* arg3 = LuaGetArgument_string(L, 3); + InitWindow(arg1, arg2, arg3); + return 0; +} + +int lua_CloseWindow(lua_State* L) +{ + CloseWindow(); + return 0; +} + +int lua_WindowShouldClose(lua_State* L) +{ + bool result = WindowShouldClose(); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsWindowMinimized(lua_State* L) +{ + bool result = IsWindowMinimized(); + lua_pushboolean(L, result); + return 1; +} + +int lua_ToggleFullscreen(lua_State* L) +{ + ToggleFullscreen(); + return 0; +} + +int lua_GetScreenWidth(lua_State* L) +{ + int result = GetScreenWidth(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetScreenHeight(lua_State* L) +{ + int result = GetScreenHeight(); + lua_pushinteger(L, result); + return 1; +} + +int lua_ShowCursor(lua_State* L) +{ + ShowCursor(); + return 0; +} + +int lua_HideCursor(lua_State* L) +{ + HideCursor(); + return 0; +} + +int lua_IsCursorHidden(lua_State* L) +{ + bool result = IsCursorHidden(); + lua_pushboolean(L, result); + return 1; +} + +// TODO: +/* +void EnableCursor(void); // Enables cursor +void DisableCursor(void); // Disables cursor +*/ + +int lua_ClearBackground(lua_State* L) +{ + Color arg1 = LuaGetArgument_Color(L, 1); + ClearBackground(arg1); + return 0; +} + +int lua_BeginDrawing(lua_State* L) +{ + BeginDrawing(); + return 0; +} + +int lua_EndDrawing(lua_State* L) +{ + EndDrawing(); + return 0; +} + +// TODO: +//void Begin2dMode(Camera2D camera); // Initialize 2D mode with custom camera +//void End2dMode(void); // Ends 2D mode custom camera usage + +int lua_Begin3dMode(lua_State* L) +{ + Camera arg1 = LuaGetArgument_Camera(L, 1); + Begin3dMode(arg1); + return 0; +} + +int lua_End3dMode(lua_State* L) +{ + End3dMode(); + return 0; +} + +// TODO: +//void BeginTextureMode(RenderTexture2D target); // Initializes render texture for drawing +//void EndTextureMode(void); // Ends drawing to render texture + +int lua_GetMouseRay(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Camera arg2 = LuaGetArgument_Camera(L, 2); + Ray result = GetMouseRay(arg1, arg2); + LuaPush_Ray(L, result); + return 1; +} + +// TODO: +//Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position from a 3d world space position +//Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix) + +#if defined(PLATFORM_WEB) + +static int LuaDrawLoopFunc; + +static void LuaDrawLoop() +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, LuaDrawLoopFunc); + lua_call(L, 0, 0); +} + +int lua_SetDrawingLoop(lua_State* L) +{ + luaL_argcheck(L, lua_isfunction(L, 1), 1, "Loop function expected"); + lua_pushvalue(L, 1); + LuaDrawLoopFunc = luaL_ref(L, LUA_REGISTRYINDEX); + SetDrawingLoop(&LuaDrawLoop); + return 0; +} + +#else + +int lua_SetTargetFPS(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + SetTargetFPS(arg1); + return 0; +} +#endif + +int lua_GetFPS(lua_State* L) +{ + float result = GetFPS(); + lua_pushnumber(L, result); + return 1; +} + +int lua_GetFrameTime(lua_State* L) +{ + float result = GetFrameTime(); + lua_pushnumber(L, result); + return 1; +} + +int lua_GetColor(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + Color result = GetColor(arg1); + LuaPush_Color(L, result); + return 1; +} + +int lua_GetHexValue(lua_State* L) +{ + Color arg1 = LuaGetArgument_Color(L, 1); + int result = GetHexValue(arg1); + lua_pushinteger(L, result); + return 1; +} + +// TODO: +/* +float *ColorToFloat(Color color); // Converts Color to float array and normalizes +float *VectorToFloat(Vector3 vec); // Converts Vector3 to float array +float *MatrixToFloat(Matrix mat); // Converts Matrix to float array +*/ + +int lua_GetRandomValue(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int result = GetRandomValue(arg1, arg2); + lua_pushinteger(L, result); + return 1; +} + +int lua_Fade(lua_State* L) +{ + Color arg1 = LuaGetArgument_Color(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Color result = Fade(arg1, arg2); + LuaPush_Color(L, result); + return 1; +} + +int lua_SetConfigFlags(lua_State* L) +{ + char arg1 = LuaGetArgument_char(L, 1); + SetConfigFlags(arg1); + return 0; +} + +int lua_ShowLogo(lua_State* L) +{ + ShowLogo(); + return 0; +} + +int lua_IsFileDropped(lua_State* L) +{ + bool result = IsFileDropped(); + lua_pushboolean(L, result); + return 1; +} +/* +int lua_*GetDroppedFiles(lua_State* L) +{ + int * arg1 = LuaGetArgument_int *(L, 1); + //char * result = *GetDroppedFiles(arg1); + LuaPush_//char *(L, result); + return 1; +} +*/ +int lua_ClearDroppedFiles(lua_State* L) +{ + ClearDroppedFiles(); + return 0; +} + +int lua_StorageSaveValue(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + StorageSaveValue(arg1, arg2); + return 0; +} + +int lua_StorageLoadValue(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int result = StorageLoadValue(arg1); + lua_pushinteger(L, result); + return 1; +} + +//------------------------------------------------------------------------------------ +// raylib [core] module functions - Input Handling +//------------------------------------------------------------------------------------ +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) +int lua_IsKeyPressed(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsKeyPressed(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsKeyDown(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsKeyDown(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsKeyReleased(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsKeyReleased(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsKeyUp(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsKeyUp(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_GetKeyPressed(lua_State* L) +{ + int result = GetKeyPressed(); + lua_pushinteger(L, result); + return 1; +} + +int lua_SetExitKey(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + SetExitKey(arg1); + return 0; +} + +int lua_IsGamepadAvailable(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsGamepadAvailable(arg1); + lua_pushboolean(L, result); + return 1; +} + +// TODO: Review +// float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis +/* +int lua_GetGamepadMovement(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + Vector2 result = GetGamepadMovement(arg1); + LuaPush_Vector2(L, result); + return 1; +} +*/ + +int lua_IsGamepadButtonPressed(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + bool result = IsGamepadButtonPressed(arg1, arg2); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsGamepadButtonDown(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + bool result = IsGamepadButtonDown(arg1, arg2); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsGamepadButtonReleased(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + bool result = IsGamepadButtonReleased(arg1, arg2); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsGamepadButtonUp(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + bool result = IsGamepadButtonUp(arg1, arg2); + lua_pushboolean(L, result); + return 1; +} +#endif + +int lua_IsMouseButtonPressed(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsMouseButtonPressed(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsMouseButtonDown(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsMouseButtonDown(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsMouseButtonReleased(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsMouseButtonReleased(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_IsMouseButtonUp(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsMouseButtonUp(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_GetMouseX(lua_State* L) +{ + int result = GetMouseX(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetMouseY(lua_State* L) +{ + int result = GetMouseY(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetMousePosition(lua_State* L) +{ + Vector2 result = GetMousePosition(); + LuaPush_Vector2(L, result); + return 1; +} + +int lua_SetMousePosition(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + SetMousePosition(arg1); + return 0; +} + +int lua_GetMouseWheelMove(lua_State* L) +{ + int result = GetMouseWheelMove(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetTouchX(lua_State* L) +{ + int result = GetTouchX(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetTouchY(lua_State* L) +{ + int result = GetTouchY(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetTouchPosition(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + Vector2 result = GetTouchPosition(arg1); + LuaPush_Vector2(L, result); + return 1; +} + +// TODO: +/* +#if defined(PLATFORM_ANDROID) +bool IsButtonPressed(int button); // Detect if an android physic button has been pressed +bool IsButtonDown(int button); // Detect if an android physic button is being pressed +bool IsButtonReleased(int button); // Detect if an android physic button has been released +#endif +*/ + +//------------------------------------------------------------------------------------ +// raylib [gestures] module functions - Gestures and Touch Handling +//------------------------------------------------------------------------------------ +int lua_SetGesturesEnabled(lua_State* L) +{ + unsigned arg1 = LuaGetArgument_unsigned(L, 1); + SetGesturesEnabled(arg1); + return 0; +} + +int lua_IsGestureDetected(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + bool result = IsGestureDetected(arg1); + lua_pushboolean(L, result); + return 1; +} + +// TODO: +///void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures + +int lua_UpdateGestures(lua_State* L) +{ + UpdateGestures(); + return 0; +} + +int lua_GetTouchPointsCount(lua_State* L) +{ + int result = GetTouchPointsCount(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetGestureDetected(lua_State* L) +{ + int result = GetGestureDetected(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetGestureHoldDuration(lua_State* L) +{ + int result = GetGestureHoldDuration(); + lua_pushinteger(L, result); + return 1; +} + +int lua_GetGestureDragVector(lua_State* L) +{ + Vector2 result = GetGestureDragVector(); + LuaPush_Vector2(L, result); + return 1; +} + +int lua_GetGestureDragAngle(lua_State* L) +{ + float result = GetGestureDragAngle(); + lua_pushnumber(L, result); + return 1; +} + +int lua_GetGesturePinchVector(lua_State* L) +{ + Vector2 result = GetGesturePinchVector(); + LuaPush_Vector2(L, result); + return 1; +} + +int lua_GetGesturePinchAngle(lua_State* L) +{ + float result = GetGesturePinchAngle(); + lua_pushnumber(L, result); + return 1; +} + +//------------------------------------------------------------------------------------ +// raylib [camera] module functions - Camera System +//------------------------------------------------------------------------------------ +int lua_SetCameraMode(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + SetCameraMode(arg1); + return 0; +} + +int lua_UpdateCamera(lua_State* L) +{ + Camera arg1 = LuaGetArgument_Camera(L, 1); + UpdateCamera(&arg1); + LuaPush_Camera(L, arg1); + return 1; +} + +int lua_UpdateCameraPlayer(lua_State* L) +{ + Camera arg1 = LuaGetArgument_Camera(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + UpdateCameraPlayer(&arg1, &arg2); + LuaPush_Camera(L, arg1); + LuaPush_Vector3(L, arg2); + return 2; +} + +int lua_SetCameraPosition(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + SetCameraPosition(arg1); + return 0; +} + +int lua_SetCameraTarget(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + SetCameraTarget(arg1); + return 0; +} + +int lua_SetCameraFovy(lua_State* L) +{ + float arg1 = LuaGetArgument_float(L, 1); + SetCameraFovy(arg1); + return 0; +} + +int lua_SetCameraPanControl(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + SetCameraPanControl(arg1); + return 0; +} + +int lua_SetCameraAltControl(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + SetCameraAltControl(arg1); + return 0; +} + +int lua_SetCameraSmoothZoomControl(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + SetCameraSmoothZoomControl(arg1); + return 0; +} + +int lua_SetCameraMoveControls(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + int arg5 = LuaGetArgument_int(L, 5); + int arg6 = LuaGetArgument_int(L, 6); + SetCameraMoveControls(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_SetCameraMouseSensitivity(lua_State* L) +{ + float arg1 = LuaGetArgument_float(L, 1); + SetCameraMouseSensitivity(arg1); + return 0; +} + +//------------------------------------------------------------------------------------ +// raylib [shapes] module functions - Basic Shapes Drawing +//------------------------------------------------------------------------------------ +int lua_DrawPixel(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawPixel(arg1, arg2, arg3); + return 0; +} + +int lua_DrawPixelV(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Color arg2 = LuaGetArgument_Color(L, 2); + DrawPixelV(arg1, arg2); + return 0; +} + +int lua_DrawLine(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawLine(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawLineV(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawLineV(arg1, arg2, arg3); + return 0; +} + +int lua_DrawCircle(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawCircle(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawCircleGradient(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawCircleGradient(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawCircleV(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawCircleV(arg1, arg2, arg3); + return 0; +} + +int lua_DrawCircleLines(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawCircleLines(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawRectangle(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawRectangle(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawRectangleRec(lua_State* L) +{ + Rectangle arg1 = LuaGetArgument_Rectangle(L, 1); + Color arg2 = LuaGetArgument_Color(L, 2); + DrawRectangleRec(arg1, arg2); + return 0; +} + +int lua_DrawRectangleGradient(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawRectangleGradient(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_DrawRectangleV(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawRectangleV(arg1, arg2, arg3); + return 0; +} + +int lua_DrawRectangleLines(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawRectangleLines(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawTriangle(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + Vector2 arg3 = LuaGetArgument_Vector2(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawTriangle(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawTriangleLines(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + Vector2 arg3 = LuaGetArgument_Vector2(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawTriangleLines(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawPoly(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawPoly(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +#define GET_TABLE(type, name, index) \ + type* name = 0; \ + size_t name##_size = 0; \ + { \ + size_t sz = 0; \ + luaL_checktype(L, index, LUA_TTABLE); \ + lua_pushnil(L); \ + while (lua_next(L, index)) { \ + LuaGetArgument_##type(L, -1); \ + sz++; \ + lua_pop(L, 1); \ + } \ + lua_pop(L, 1); \ + name = calloc(sz, sizeof(type)); \ + sz = 0; \ + lua_pushnil(L); \ + while (lua_next(L, index)) { \ + name[sz] = LuaGetArgument_##type(L, -1); \ + sz++; \ + lua_pop(L, 1); \ + } \ + lua_pop(L, 1); \ + name##_size = sz; \ + } + + +int lua_DrawPolyEx(lua_State* L) +{ + GET_TABLE(Vector2, arg1, 1); + Color arg2 = LuaGetArgument_Color(L, 2); + DrawPolyEx(arg1, arg1_size, arg2); + free(arg1); + return 0; +} + +int lua_DrawPolyExLines(lua_State* L) +{ + GET_TABLE(Vector2, arg1, 1); + Color arg2 = LuaGetArgument_Color(L, 2); + DrawPolyExLines(arg1, arg1_size, arg2); + free(arg1); + return 0; +} + +int lua_CheckCollisionRecs(lua_State* L) +{ + Rectangle arg1 = LuaGetArgument_Rectangle(L, 1); + Rectangle arg2 = LuaGetArgument_Rectangle(L, 2); + bool result = CheckCollisionRecs(arg1, arg2); + lua_pushboolean(L, result); + return 1; +} + +int lua_CheckCollisionCircles(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Vector2 arg3 = LuaGetArgument_Vector2(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + bool result = CheckCollisionCircles(arg1, arg2, arg3, arg4); + lua_pushboolean(L, result); + return 1; +} + +int lua_CheckCollisionCircleRec(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Rectangle arg3 = LuaGetArgument_Rectangle(L, 3); + bool result = CheckCollisionCircleRec(arg1, arg2, arg3); + lua_pushboolean(L, result); + return 1; +} + +int lua_GetCollisionRec(lua_State* L) +{ + Rectangle arg1 = LuaGetArgument_Rectangle(L, 1); + Rectangle arg2 = LuaGetArgument_Rectangle(L, 2); + Rectangle result = GetCollisionRec(arg1, arg2); + LuaPush_Rectangle(L, result); + return 1; +} + +int lua_CheckCollisionPointRec(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Rectangle arg2 = LuaGetArgument_Rectangle(L, 2); + bool result = CheckCollisionPointRec(arg1, arg2); + lua_pushboolean(L, result); + return 1; +} + +int lua_CheckCollisionPointCircle(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + bool result = CheckCollisionPointCircle(arg1, arg2, arg3); + lua_pushboolean(L, result); + return 1; +} + +int lua_CheckCollisionPointTriangle(lua_State* L) +{ + Vector2 arg1 = LuaGetArgument_Vector2(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + Vector2 arg3 = LuaGetArgument_Vector2(L, 3); + Vector2 arg4 = LuaGetArgument_Vector2(L, 4); + bool result = CheckCollisionPointTriangle(arg1, arg2, arg3, arg4); + lua_pushboolean(L, result); + return 1; +} + +//------------------------------------------------------------------------------------ +// raylib [textures] module functions - Texture Loading and Drawing +//------------------------------------------------------------------------------------ +int lua_LoadImage(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + Image result = LoadImage(arg1); + LuaPush_Image(L, result); + return 1; +} + +int lua_LoadImageEx(lua_State* L) +{ + //Color * arg1 = LuaGetArgument_Color *(L, 1); + GET_TABLE(Color, arg1, 1); + + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + Image result = LoadImageEx(arg1, arg2, arg3); + LuaPush_Image(L, result); + + free(arg1); + return 1; +} + +int lua_LoadImageRaw(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + int arg5 = LuaGetArgument_int(L, 5); + Image result = LoadImageRaw(arg1, arg2, arg3, arg4, arg5); + LuaPush_Image(L, result); + return 1; +} + +int lua_LoadImageFromRES(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + Image result = LoadImageFromRES(arg1, arg2); + LuaPush_Image(L, result); + return 1; +} + +int lua_LoadTexture(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + Texture2D result = LoadTexture(arg1); + LuaPush_Texture2D(L, result); + return 1; +} + +// TODO: Texture2D LoadTextureEx(void *data, int width, int height, int textureFormat); +int lua_LoadTextureEx(lua_State* L) +{ + void * arg1 = LuaGetArgument_string(L, 1); // TODO: How to get a void * ? + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Texture2D result = LoadTextureEx(arg1, arg2, arg3, arg4); + LuaPush_Texture2D(L, result); + return 1; +} + +int lua_LoadTextureFromRES(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + Texture2D result = LoadTextureFromRES(arg1, arg2); + LuaPush_Texture2D(L, result); + return 1; +} + +int lua_LoadTextureFromImage(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + Texture2D result = LoadTextureFromImage(arg1); + LuaPush_Texture2D(L, result); + return 1; +} + +int lua_LoadRenderTexture(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + RenderTexture2D result = LoadRenderTexture(arg1, arg2); + LuaPush_RenderTexture2D(L, result); + return 1; +} + +int lua_UnloadImage(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + UnloadImage(arg1); + return 0; +} + +int lua_UnloadTexture(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + UnloadTexture(arg1); + return 0; +} + +int lua_UnloadRenderTexture(lua_State* L) +{ + RenderTexture2D arg1 = LuaGetArgument_RenderTexture2D(L, 1); + UnloadRenderTexture(arg1); + return 0; +} + +int lua_GetImageData(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + Color * result = GetImageData(arg1); + lua_createtable(L, arg1.width*arg1.height, 0); + for (int i = 0; i < arg1.width*arg1.height; i++) + { + LuaPush_Color(L, result[i]); + lua_rawseti(L, -2, i + 1); + } + free(result); + return 1; +} + +int lua_GetTextureData(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + Image result = GetTextureData(arg1); + LuaPush_Image(L, result); + return 1; +} + +int lua_ImageToPOT(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + Color arg2 = LuaGetArgument_Color(L, 2); + ImageToPOT(&arg1, arg2); + LuaPush_Image(L, arg1); + return 1; +} + +int lua_ImageFormat(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + ImageFormat(&arg1, arg2); + LuaPush_Image(L, arg1); + return 1; +} + +// TODO: +/* +void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) +Image ImageCopy(Image image); // Create an image duplicate (useful for transformations) +void ImageCrop(Image *image, Rectangle crop); // Crop an image to a defined rectangle +void ImageResize(Image *image, int newWidth, int newHeight); // Resize and image (bilinear filtering) +void ImageResizeNN(Image *image,int newWidth,int newHeight); // Resize and image (Nearest-Neighbor scaling algorithm) +Image ImageText(const char *text, int fontSize, Color color); // Create an image from text (default font) +Image ImageTextEx(SpriteFont font, const char *text, int fontSize, int spacing, Color tint); // Create an image from text (custom sprite font) +void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec); // Draw a source image within a destination image +void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color); // Draw text (default font) within an image (destination) +void ImageDrawTextEx(Image *dst, Vector2 position, SpriteFont font, const char *text, int fontSize, int spacing, Color color); // Draw text (custom sprite font) within an image (destination) +void ImageFlipVertical(Image *image); // Flip image vertically +void ImageFlipHorizontal(Image *image); // Flip image horizontally +void ImageColorTint(Image *image, Color color); // Modify image color: tint +void ImageColorInvert(Image *image); // Modify image color: invert +void ImageColorGrayscale(Image *image); // Modify image color: grayscale +void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100) +void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255) +*/ + +int lua_GenTextureMipmaps(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + GenTextureMipmaps(arg1); + return 0; +} + +// TODO: void UpdateTexture(Texture2D texture, void *pixels); // Update GPU texture with new data + +int lua_DrawTexture(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawTexture(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawTextureV(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawTextureV(arg1, arg2, arg3); + return 0; +} + +int lua_DrawTextureEx(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawTextureEx(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawTextureRec(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + Rectangle arg2 = LuaGetArgument_Rectangle(L, 2); + Vector2 arg3 = LuaGetArgument_Vector2(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawTextureRec(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawTexturePro(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + Rectangle arg2 = LuaGetArgument_Rectangle(L, 2); + Rectangle arg3 = LuaGetArgument_Rectangle(L, 3); + Vector2 arg4 = LuaGetArgument_Vector2(L, 4); + float arg5 = LuaGetArgument_float(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawTexturePro(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +//------------------------------------------------------------------------------------ +// raylib [text] module functions - Font Loading and Text Drawing +//------------------------------------------------------------------------------------ +int lua_GetDefaultFont(lua_State* L) +{ + SpriteFont result = GetDefaultFont(); + LuaPush_SpriteFont(L, result); + return 1; +} + +int lua_LoadSpriteFont(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + SpriteFont result = LoadSpriteFont(arg1); + LuaPush_SpriteFont(L, result); + return 1; +} + +int lua_UnloadSpriteFont(lua_State* L) +{ + SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1); + UnloadSpriteFont(arg1); + return 0; +} + +int lua_DrawText(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawText(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawTextEx(lua_State* L) +{ + SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1); + const char* arg2 = LuaGetArgument_string(L, 2); + Vector2 arg3 = LuaGetArgument_Vector2(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + int arg5 = LuaGetArgument_int(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawTextEx(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_MeasureText(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + int result = MeasureText(arg1, arg2); + lua_pushinteger(L, result); + return 1; +} + +int lua_MeasureTextEx(lua_State* L) +{ + SpriteFont arg1 = LuaGetArgument_SpriteFont(L, 1); + const char * arg2 = LuaGetArgument_string(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Vector2 result = MeasureTextEx(arg1, arg2, arg3, arg4); + LuaPush_Vector2(L, result); + return 1; +} + +int lua_DrawFPS(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + DrawFPS(arg1, arg2); + return 0; +} + +// TODO: +/* +const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' +const char *SubText(const char *text, int position, int length); // Get a piece of a text string +*/ + +int lua_DrawCube(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawCube(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawCubeV(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawCubeV(arg1, arg2, arg3); + return 0; +} + +int lua_DrawCubeWires(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawCubeWires(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawCubeTexture(lua_State* L) +{ + Texture2D arg1 = LuaGetArgument_Texture2D(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + float arg5 = LuaGetArgument_float(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawCubeTexture(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_DrawSphere(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawSphere(arg1, arg2, arg3); + return 0; +} + +int lua_DrawSphereEx(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawSphereEx(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawSphereWires(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawSphereWires(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawCylinder(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + int arg5 = LuaGetArgument_int(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawCylinder(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_DrawCylinderWires(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + int arg5 = LuaGetArgument_int(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawCylinderWires(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_DrawPlane(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector2 arg2 = LuaGetArgument_Vector2(L, 2); + Color arg3 = LuaGetArgument_Color(L, 3); + DrawPlane(arg1, arg2, arg3); + return 0; +} + +int lua_DrawRay(lua_State* L) +{ + Ray arg1 = LuaGetArgument_Ray(L, 1); + Color arg2 = LuaGetArgument_Color(L, 2); + DrawRay(arg1, arg2); + return 0; +} + +int lua_DrawGrid(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + DrawGrid(arg1, arg2); + return 0; +} + +int lua_DrawGizmo(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + DrawGizmo(arg1); + return 0; +} + +// TODO: +/* +void DrawLight(Light light); // Draw light in 3D world +void Draw3DLine(Vector3 startPos, Vector3 endPos, Color color); // Draw a line in 3D world space +void Draw3DCircle(Vector3 center, float radius, float rotationAngle, Vector3 rotation, Color color); // Draw a circle in 3D world space +*/ + +//------------------------------------------------------------------------------------ +// raylib [models] module functions +//------------------------------------------------------------------------------------ +int lua_LoadModel(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + Model result = LoadModel(arg1); + LuaPush_Model(L, result); + return 1; +} + +int lua_LoadModelEx(lua_State* L) +{ + Mesh arg1 = LuaGetArgument_Mesh(L, 1); + bool arg2 = LuaGetArgument_int(L, 2); + Model result = LoadModelEx(arg1, arg2); + LuaPush_Model(L, result); + return 1; +} + +int lua_LoadModelFromRES(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + Model result = LoadModelFromRES(arg1, arg2); + LuaPush_Model(L, result); + return 1; +} + +int lua_LoadHeightmap(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Model result = LoadHeightmap(arg1, arg2); + LuaPush_Model(L, result); + return 1; +} + +int lua_LoadCubicmap(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + Model result = LoadCubicmap(arg1); + LuaPush_Model(L, result); + return 1; +} + +int lua_UnloadModel(lua_State* L) +{ + Model arg1 = LuaGetArgument_Model(L, 1); + UnloadModel(arg1); + return 0; +} + +// TODO: GenMesh*() + +int lua_LoadMaterial(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + Material result = LoadMaterial(arg1); + LuaPush_Material(L, result); + return 1; +} + +int lua_LoadDefaultMaterial(lua_State* L) +{ + Material result = LoadDefaultMaterial(); + LuaPush_Material(L, result); + return 1; +} + +int lua_LoadStandardMaterial(lua_State* L) +{ + Material result = LoadStandardMaterial(); + LuaPush_Material(L, result); + return 1; +} + +int lua_UnloadMaterial(lua_State* L) +{ + Material arg1 = LuaGetArgument_Material(L, 1); + UnloadMaterial(arg1); + return 0; +} + +int lua_DrawModel(lua_State* L) +{ + Model arg1 = LuaGetArgument_Model(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawModel(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawModelEx(lua_State* L) +{ + Model arg1 = LuaGetArgument_Model(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 arg3 = LuaGetArgument_Vector3(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Vector3 arg5 = LuaGetArgument_Vector3(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawModelEx(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_DrawModelWires(lua_State* L) +{ + Model arg1 = LuaGetArgument_Model(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Color arg4 = LuaGetArgument_Color(L, 4); + DrawModelWires(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_DrawModelWiresEx(lua_State* L) +{ + Model arg1 = LuaGetArgument_Model(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 arg3 = LuaGetArgument_Vector3(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Vector3 arg5 = LuaGetArgument_Vector3(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawModelWiresEx(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_DrawBillboard(lua_State* L) +{ + Camera arg1 = LuaGetArgument_Camera(L, 1); + Texture2D arg2 = LuaGetArgument_Texture2D(L, 2); + Vector3 arg3 = LuaGetArgument_Vector3(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Color arg5 = LuaGetArgument_Color(L, 5); + DrawBillboard(arg1, arg2, arg3, arg4, arg5); + return 0; +} + +int lua_DrawBillboardRec(lua_State* L) +{ + Camera arg1 = LuaGetArgument_Camera(L, 1); + Texture2D arg2 = LuaGetArgument_Texture2D(L, 2); + Rectangle arg3 = LuaGetArgument_Rectangle(L, 3); + Vector3 arg4 = LuaGetArgument_Vector3(L, 4); + float arg5 = LuaGetArgument_float(L, 5); + Color arg6 = LuaGetArgument_Color(L, 6); + DrawBillboardRec(arg1, arg2, arg3, arg4, arg5, arg6); + return 0; +} + +int lua_CalculateBoundingBox(lua_State* L) +{ + Mesh arg1 = LuaGetArgument_Mesh(L, 1); + BoundingBox result = CalculateBoundingBox(arg1); + LuaPush_BoundingBox(L, result); + return 0; +} + +int lua_CheckCollisionSpheres(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Vector3 arg3 = LuaGetArgument_Vector3(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + bool result = CheckCollisionSpheres(arg1, arg2, arg3, arg4); + lua_pushboolean(L, result); + return 1; +} + +int lua_CheckCollisionBoxes(lua_State* L) +{ + BoundingBox arg1 = LuaGetArgument_BoundingBox(L, 1); + BoundingBox arg2 = LuaGetArgument_BoundingBox(L, 2); + bool result = CheckCollisionBoxes(arg1, arg2); + lua_pushboolean(L, result); + return 1; +} + +int lua_CheckCollisionBoxSphere(lua_State* L) +{ + BoundingBox arg1 = LuaGetArgument_BoundingBox(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + float arg3 = LuaGetArgument_float(L, 4); + bool result = CheckCollisionBoxSphere(arg1, arg2, arg3); + lua_pushboolean(L, result); + return 1; +} + +// TODO: +/* +bool CheckCollisionRaySphere(Ray ray, Vector3 spherePosition, float sphereRadius); // Detect collision between ray and sphere +bool CheckCollisionRaySphereEx(Ray ray, Vector3 spherePosition, float sphereRadius, Vector3 *collisionPoint); // Detect collision between ray and sphere with extended parameters and collision point detection +bool CheckCollisionRayBox(Ray ray, BoundingBox box); // Detect collision between ray and box +Vector3 ResolveCollisionCubicmap(Image cubicmap, Vector3 mapPosition, Vector3 *playerPosition, float radius); // Detect collision of player radius with cubicmap +*/ + +int lua_ResolveCollisionCubicmap(lua_State* L) +{ + Image arg1 = LuaGetArgument_Image(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 arg3 = LuaGetArgument_Vector3(L, 3); + float arg4 = LuaGetArgument_float(L, 4); + Vector3 result = ResolveCollisionCubicmap(arg1, arg2, &arg3, arg4); + LuaPush_Vector3(L, result); + LuaPush_Vector3(L, arg3); + return 2; +} + +//------------------------------------------------------------------------------------ +// raylib [raymath] module functions - Shaders +//------------------------------------------------------------------------------------ +int lua_LoadShader(lua_State* L) +{ + char * arg1 = (char*)LuaGetArgument_string(L, 1); + char * arg2 = (char*)LuaGetArgument_string(L, 2); + Shader result = LoadShader(arg1, arg2); + LuaPush_Shader(L, result); + return 1; +} + +int lua_UnloadShader(lua_State* L) +{ + Shader arg1 = LuaGetArgument_Shader(L, 1); + UnloadShader(arg1); + return 0; +} + +int lua_GetDefaultShader(lua_State* L) +{ + Shader result = GetDefaultShader(); + LuaPush_Shader(L, result); + return 1; +} + +int lua_GetStandardShader(lua_State* L) +{ + Shader result = GetStandardShader(); + LuaPush_Shader(L, result); + return 1; +} + +int lua_GetDefaultTexture(lua_State* L) +{ + Texture2D result = GetDefaultTexture(); + LuaPush_Texture2D(L, result); + return 1; +} + +int lua_GetShaderLocation(lua_State* L) +{ + Shader arg1 = LuaGetArgument_Shader(L, 1); + const char * arg2 = LuaGetArgument_string(L, 2); + int result = GetShaderLocation(arg1, arg2); + lua_pushinteger(L, result); + return 1; +} + +int lua_SetShaderValue(lua_State* L) +{ + Shader arg1 = LuaGetArgument_Shader(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + GET_TABLE(float, arg3, 3); + SetShaderValue(arg1, arg2, arg3, arg3_size); + free(arg3); + return 0; +} + +int lua_SetShaderValuei(lua_State* L) +{ + Shader arg1 = LuaGetArgument_Shader(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + GET_TABLE(int, arg3, 3); + SetShaderValuei(arg1, arg2, arg3, arg3_size); + free(arg3); + return 0; +} + +int lua_SetShaderValueMatrix(lua_State* L) +{ + Shader arg1 = LuaGetArgument_Shader(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + Matrix arg3 = LuaGetArgument_Matrix(L, 3); + SetShaderValueMatrix(arg1, arg2, arg3); + return 0; +} + +int lua_SetMatrixProjection(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + SetMatrixProjection(arg1); + return 0; +} + +int lua_SetMatrixModelview(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + SetMatrixModelview(arg1); + return 0; +} + +int lua_BeginShaderMode(lua_State* L) +{ + Shader arg1 = LuaGetArgument_Shader(L, 1); + BeginShaderMode(arg1); + return 0; +} + +int lua_EndShaderMode(lua_State* L) +{ + EndShaderMode(); + return 0; +} + +int lua_BeginBlendMode(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + BeginBlendMode(arg1); + return 0; +} + +int lua_EndBlendMode(lua_State* L) +{ + EndBlendMode(); + return 0; +} + +// TODO: +//Light CreateLight(int type, Vector3 position, Color diffuse); // Create a new light, initialize it and add to pool +//void DestroyLight(Light light); // Destroy a light and take it out of the list + +// TODO: +/* +//------------------------------------------------------------------------------------ +// raylib [rlgl] module functions - VR experience +//------------------------------------------------------------------------------------ +void InitVrDevice(int vdDevice); // Init VR device +void CloseVrDevice(void); // Close VR device +void UpdateVrTracking(void); // Update VR tracking (position and orientation) +void BeginVrDrawing(void); // Begin VR drawing configuration +void EndVrDrawing(void); // End VR drawing process (and desktop mirror) +bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready +void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator) +*/ + +//------------------------------------------------------------------------------------ +// raylib [audio] module functions +//------------------------------------------------------------------------------------ +int lua_InitAudioDevice(lua_State* L) +{ + InitAudioDevice(); + return 0; +} + +int lua_CloseAudioDevice(lua_State* L) +{ + CloseAudioDevice(); + return 0; +} + +int lua_IsAudioDeviceReady(lua_State* L) +{ + bool result = IsAudioDeviceReady(); + lua_pushboolean(L, result); + return 0; +} + +int lua_LoadSound(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + Sound result = LoadSound((char*)arg1); + LuaPush_Sound(L, result); + return 1; +} + +int lua_LoadSoundFromWave(lua_State* L) +{ + Wave arg1 = LuaGetArgument_Wave(L, 1); + Sound result = LoadSoundFromWave(arg1); + LuaPush_Sound(L, result); + return 1; +} + +int lua_LoadSoundFromRES(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + int arg2 = LuaGetArgument_int(L, 2); + Sound result = LoadSoundFromRES(arg1, arg2); + LuaPush_Sound(L, result); + return 1; +} + +int lua_UnloadSound(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + UnloadSound(arg1); + return 0; +} + +int lua_PlaySound(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + PlaySound(arg1); + return 0; +} + +int lua_PauseSound(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + PauseSound(arg1); + return 0; +} + +int lua_ResumeSound(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + ResumeSound(arg1); + return 0; +} + +int lua_StopSound(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + StopSound(arg1); + return 0; +} + +int lua_IsSoundPlaying(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + bool result = IsSoundPlaying(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_SetSoundVolume(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + SetSoundVolume(arg1, arg2); + return 0; +} + +int lua_SetSoundPitch(lua_State* L) +{ + Sound arg1 = LuaGetArgument_Sound(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + SetSoundPitch(arg1, arg2); + return 0; +} + +int lua_LoadMusicStream(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + Music result = LoadMusicStream((char *)arg1); + LuaPush_Music(L, result); + return 0; +} + +int lua_UnloadMusicStream(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + UnloadMusicStream(arg1); + return 0; +} + +int lua_PlayMusicStream(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + PlayMusicStream(arg1); + return 0; +} + +int lua_UpdateMusicStream(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + UpdateMusicStream(arg1); + return 0; +} + +int lua_StopMusicStream(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + StopMusicStream(arg1); + return 0; +} + +int lua_PauseMusicStream(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + PauseMusicStream(arg1); + return 0; +} + +int lua_ResumeMusicStream(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + ResumeMusicStream(arg1); + return 0; +} + +int lua_IsMusicPlaying(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + bool result = IsMusicPlaying(arg1); + lua_pushboolean(L, result); + return 1; +} + +int lua_SetMusicVolume(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + SetMusicVolume(arg1, arg2); + return 0; +} + +int lua_GetMusicTimeLength(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + float result = GetMusicTimeLength(arg1); + lua_pushnumber(L, result); + return 1; +} + +int lua_GetMusicTimePlayed(lua_State* L) +{ + Music arg1 = LuaGetArgument_Music(L, 1); + float result = GetMusicTimePlayed(arg1); + lua_pushnumber(L, result); + return 1; +} + +// TODO: +/* +AudioStream InitAudioStream(unsigned int sampleRate, + unsigned int sampleSize, + unsigned int channels); // Init audio stream (to stream audio pcm data) +void UpdateAudioStream(AudioStream stream, void *data, int numSamples); // Update audio stream buffers with data +void CloseAudioStream(AudioStream stream); // Close audio stream and free memory +bool IsAudioBufferProcessed(AudioStream stream); // Check if any audio stream buffers requires refill +void PlayAudioStream(AudioStream stream); // Play audio stream +void PauseAudioStream(AudioStream stream); // Pause audio stream +void ResumeAudioStream(AudioStream stream); // Resume audio stream +void StopAudioStream(AudioStream stream); // Stop audio stream +*/ + +//---------------------------------------------------------------------------------- +// raylib [utils] module functions +//---------------------------------------------------------------------------------- +int lua_DecompressData(lua_State* L) +{ + unsigned char *arg1 = (unsigned char *)LuaGetArgument_string(L, 1); + unsigned arg2 = (unsigned)LuaGetArgument_int(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + unsigned char *result = DecompressData(arg1, arg2, arg3); + lua_pushlstring(L, (const char *)result, arg3); + return 1; +} + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) +int lua_WriteBitmap(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + unsigned char* arg2 = (unsigned char*)LuaGetArgument_string(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + WriteBitmap(arg1, arg2, arg3, arg4); + return 0; +} + +int lua_WritePNG(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + unsigned char* arg2 = (unsigned char*)LuaGetArgument_string(L, 2); + int arg3 = LuaGetArgument_int(L, 3); + int arg4 = LuaGetArgument_int(L, 4); + int arg5 = LuaGetArgument_int(L, 5); + WritePNG(arg1, arg2, arg3, arg4, arg5); + return 0; +} +#endif + +int lua_TraceLog(lua_State* L) +{ + int num_args = lua_gettop(L) - 1; + int arg1 = LuaGetArgument_int(L, 1); + + /// type, fmt, args... + + lua_rotate(L, 1, -1); /// fmt, args..., type + lua_pop(L, 1); /// fmt, args... + + lua_getglobal(L, "string"); /// fmt, args..., [string] + lua_getfield(L, 1, "format"); /// fmt, args..., [string], format() + lua_rotate(L, 1, 2); /// [string], format(), fmt, args... + lua_call(L, num_args, 1); /// [string], formatted_string + + TraceLog(arg1, "%s", luaL_checkstring(L,-1)); + return 0; +} + +int lua_GetExtension(lua_State* L) +{ + const char * arg1 = LuaGetArgument_string(L, 1); + const char* result = GetExtension(arg1); + lua_pushstring(L, result); + return 1; +} + +int lua_GetNextPOT(lua_State* L) +{ + int arg1 = LuaGetArgument_int(L, 1); + int result = GetNextPOT(arg1); + lua_pushinteger(L, result); + return 1; +} + +//---------------------------------------------------------------------------------- +// raylib [raymath] module functions - Vector3 math +//---------------------------------------------------------------------------------- +int lua_VectorAdd(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 result = VectorAdd(arg1, arg2); + LuaPush_Vector3(L, result); + return 1; +} + +int lua_VectorSubtract(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 result = VectorSubtract(arg1, arg2); + LuaPush_Vector3(L, result); + return 1; +} + +int lua_VectorCrossProduct(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 result = VectorCrossProduct(arg1, arg2); + LuaPush_Vector3(L, result); + return 1; +} + +int lua_VectorPerpendicular(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 result = VectorPerpendicular(arg1); + LuaPush_Vector3(L, result); + return 1; +} + +int lua_VectorDotProduct(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + float result = VectorDotProduct(arg1, arg2); + lua_pushnumber(L, result); + return 1; +} + +int lua_VectorLength(lua_State* L) +{ + const Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float result = VectorLength(arg1); + lua_pushnumber(L, result); + return 1; +} + +int lua_VectorScale(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + VectorScale(&arg1, arg2); + LuaPush_Vector3(L, arg1); + return 1; +} + +int lua_VectorNegate(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + VectorNegate(&arg1); + LuaPush_Vector3(L, arg1); + return 1; +} + +int lua_VectorNormalize(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + VectorNormalize(&arg1); + LuaPush_Vector3(L, arg1); + return 1; +} + +int lua_VectorDistance(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + float result = VectorDistance(arg1, arg2); + lua_pushnumber(L, result); + return 1; +} + +int lua_VectorLerp(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Vector3 result = VectorLerp(arg1, arg2, arg3); + LuaPush_Vector3(L, result); + return 1; +} + +int lua_VectorReflect(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 result = VectorReflect(arg1, arg2); + LuaPush_Vector3(L, result); + return 1; +} + +int lua_VectorTransform(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Matrix arg2 = LuaGetArgument_Matrix(L, 2); + VectorTransform(&arg1, arg2); + LuaPush_Vector3(L, arg1); + return 1; +} + +int lua_VectorZero(lua_State* L) +{ + Vector3 result = VectorZero(); + LuaPush_Vector3(L, result); + return 1; +} + +//---------------------------------------------------------------------------------- +// raylib [raymath] module functions - Matrix math +//---------------------------------------------------------------------------------- +int lua_MatrixDeterminant(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + float result = MatrixDeterminant(arg1); + lua_pushnumber(L, result); + return 1; +} + +int lua_MatrixTrace(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + float result = MatrixTrace(arg1); + lua_pushnumber(L, result); + return 1; +} + +int lua_MatrixTranspose(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + MatrixTranspose(&arg1); + LuaPush_Matrix(L, &arg1); + return 1; +} + +int lua_MatrixInvert(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + MatrixInvert(&arg1); + LuaPush_Matrix(L, &arg1); + return 1; +} + +int lua_MatrixNormalize(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + MatrixNormalize(&arg1); + LuaPush_Matrix(L, &arg1); + return 1; +} + +int lua_MatrixIdentity(lua_State* L) +{ + Matrix result = MatrixIdentity(); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixAdd(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + Matrix arg2 = LuaGetArgument_Matrix(L, 2); + Matrix result = MatrixAdd(arg1, arg2); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixSubstract(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + Matrix arg2 = LuaGetArgument_Matrix(L, 2); + Matrix result = MatrixSubstract(arg1, arg2); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixTranslate(lua_State* L) +{ + float arg1 = LuaGetArgument_float(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Matrix result = MatrixTranslate(arg1, arg2, arg3); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixRotate(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Matrix result = MatrixRotate(arg1, arg2); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixRotateX(lua_State* L) +{ + float arg1 = LuaGetArgument_float(L, 1); + Matrix result = MatrixRotateX(arg1); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixRotateY(lua_State* L) +{ + float arg1 = LuaGetArgument_float(L, 1); + Matrix result = MatrixRotateY(arg1); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixRotateZ(lua_State* L) +{ + float arg1 = LuaGetArgument_float(L, 1); + Matrix result = MatrixRotateZ(arg1); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixScale(lua_State* L) +{ + float arg1 = LuaGetArgument_float(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Matrix result = MatrixScale(arg1, arg2, arg3); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixMultiply(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + Matrix arg2 = LuaGetArgument_Matrix(L, 2); + Matrix result = MatrixMultiply(arg1, arg2); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixFrustum(lua_State* L) +{ + double arg1 = LuaGetArgument_double(L, 1); + double arg2 = LuaGetArgument_double(L, 2); + double arg3 = LuaGetArgument_double(L, 3); + double arg4 = LuaGetArgument_double(L, 4); + double arg5 = LuaGetArgument_double(L, 5); + double arg6 = LuaGetArgument_double(L, 6); + Matrix result = MatrixFrustum(arg1, arg2, arg3, arg4, arg5, arg6); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixPerspective(lua_State* L) +{ + double arg1 = LuaGetArgument_double(L, 1); + double arg2 = LuaGetArgument_double(L, 2); + double arg3 = LuaGetArgument_double(L, 3); + double arg4 = LuaGetArgument_double(L, 4); + Matrix result = MatrixPerspective(arg1, arg2, arg3, arg4); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixOrtho(lua_State* L) +{ + double arg1 = LuaGetArgument_double(L, 1); + double arg2 = LuaGetArgument_double(L, 2); + double arg3 = LuaGetArgument_double(L, 3); + double arg4 = LuaGetArgument_double(L, 4); + double arg5 = LuaGetArgument_double(L, 5); + double arg6 = LuaGetArgument_double(L, 6); + Matrix result = MatrixOrtho(arg1, arg2, arg3, arg4, arg5, arg6); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_MatrixLookAt(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + Vector3 arg2 = LuaGetArgument_Vector3(L, 2); + Vector3 arg3 = LuaGetArgument_Vector3(L, 3); + Matrix result = MatrixLookAt(arg1, arg2, arg3); + LuaPush_Matrix(L, &result); + return 1; +} + +//---------------------------------------------------------------------------------- +// raylib [raymath] module functions - Quaternion math +//---------------------------------------------------------------------------------- +int lua_QuaternionLength(lua_State* L) +{ + Quaternion arg1 = LuaGetArgument_Quaternion(L, 1); + float result = QuaternionLength(arg1); + lua_pushnumber(L, result); + return 1; +} + +int lua_QuaternionNormalize(lua_State* L) +{ + Quaternion arg1 = LuaGetArgument_Quaternion(L, 1); + QuaternionNormalize(&arg1); + LuaPush_Quaternion(L, arg1); + return 1; +} + +int lua_QuaternionMultiply(lua_State* L) +{ + Quaternion arg1 = LuaGetArgument_Quaternion(L, 1); + Quaternion arg2 = LuaGetArgument_Quaternion(L, 2); + Quaternion result = QuaternionMultiply(arg1, arg2); + LuaPush_Quaternion(L, result); + return 1; +} + +int lua_QuaternionSlerp(lua_State* L) +{ + Quaternion arg1 = LuaGetArgument_Quaternion(L, 1); + Quaternion arg2 = LuaGetArgument_Quaternion(L, 2); + float arg3 = LuaGetArgument_float(L, 3); + Quaternion result = QuaternionSlerp(arg1, arg2, arg3); + LuaPush_Quaternion(L, result); + return 1; +} + +int lua_QuaternionFromMatrix(lua_State* L) +{ + Matrix arg1 = LuaGetArgument_Matrix(L, 1); + Quaternion result = QuaternionFromMatrix(arg1); + LuaPush_Quaternion(L, result); + return 1; +} + +int lua_QuaternionToMatrix(lua_State* L) +{ + Quaternion arg1 = LuaGetArgument_Quaternion(L, 1); + Matrix result = QuaternionToMatrix(arg1); + LuaPush_Matrix(L, &result); + return 1; +} + +int lua_QuaternionFromAxisAngle(lua_State* L) +{ + Vector3 arg1 = LuaGetArgument_Vector3(L, 1); + float arg2 = LuaGetArgument_float(L, 2); + Quaternion result = QuaternionFromAxisAngle(arg1, arg2); + LuaPush_Quaternion(L, result); + return 1; +} + +int lua_QuaternionToAxisAngle(lua_State* L) +{ + Quaternion arg1 = LuaGetArgument_Quaternion(L, 1); + Vector3 arg2; + float arg3 = 0; + QuaternionToAxisAngle(arg1, &arg2, &arg3); + LuaPush_Vector3(L, arg2); + lua_pushnumber(L, arg3); + return 2; +} + +int lua_QuaternionTransform(lua_State* L) +{ + Quaternion arg1 = LuaGetArgument_Quaternion(L, 1); + Matrix arg2 = LuaGetArgument_Matrix(L, 2); + QuaternionTransform(&arg1, arg2); + LuaPush_Quaternion(L, arg1); + return 1; +} + + +//---------------------------------------------------------------------------------- +// Functions Registering +//---------------------------------------------------------------------------------- +#define REG(name) { #name, lua_##name }, + +// raylib Functions (and data types) list +static luaL_Reg raylib_functions[] = { + REG(Color) + REG(Vector2) + REG(Vector3) + REG(Rectangle) + REG(Ray) + REG(Camera) + // TODO: Additional structs + + // TODO: Review registered functions + REG(InitWindow) + REG(CloseWindow) + REG(WindowShouldClose) + REG(IsWindowMinimized) + REG(ToggleFullscreen) + + REG(GetScreenWidth) + REG(GetScreenHeight) + REG(ClearBackground) + REG(BeginDrawing) + REG(EndDrawing) + REG(Begin3dMode) + REG(End3dMode) + REG(GetMouseRay) +#if defined(PLATFORM_WEB) + REG(SetDrawingLoop) +#else + REG(SetTargetFPS) +#endif + REG(GetFPS) + REG(GetFrameTime) + REG(GetColor) + REG(GetHexValue) + REG(GetRandomValue) + REG(Fade) + REG(SetConfigFlags) + REG(ShowLogo) + REG(IsFileDropped) + //REG(*GetDroppedFiles) + REG(ClearDroppedFiles) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB) + REG(IsKeyPressed) + REG(IsKeyDown) + REG(IsKeyReleased) + REG(IsKeyUp) + REG(GetKeyPressed) + REG(IsMouseButtonPressed) + REG(IsMouseButtonDown) + REG(IsMouseButtonReleased) + REG(IsMouseButtonUp) + REG(GetMouseX) + REG(GetMouseY) + REG(GetMousePosition) + REG(SetMousePosition) + REG(GetMouseWheelMove) + REG(ShowCursor) + REG(HideCursor) + REG(IsCursorHidden) +#endif + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + REG(IsGamepadAvailable) + + REG(IsGamepadButtonPressed) + REG(IsGamepadButtonDown) + REG(IsGamepadButtonReleased) + REG(IsGamepadButtonUp) +#endif + +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) + REG(GetTouchX) + REG(GetTouchY) + REG(GetTouchPosition) +#if defined(PLATFORM_WEB) + REG(InitGesturesSystem) +#elif defined(PLATFORM_ANDROID) + //REG(InitGesturesSystem) +#endif + REG(UpdateGestures) + REG(IsGestureDetected) + REG(GetGestureType) + REG(SetGesturesEnabled) + REG(GetGestureDragIntensity) + REG(GetGestureDragAngle) + REG(GetGestureDragVector) + REG(GetGestureHoldDuration) + REG(GetGesturePinchDelta) + REG(GetGesturePinchAngle) +#endif + REG(SetCameraMode) + REG(UpdateCamera) + REG(UpdateCameraPlayer) + REG(SetCameraPosition) + REG(SetCameraTarget) + REG(SetCameraPanControl) + REG(SetCameraAltControl) + REG(SetCameraSmoothZoomControl) + REG(SetCameraMoveControls) + REG(SetCameraMouseSensitivity) + REG(DrawPixel) + REG(DrawPixelV) + REG(DrawLine) + REG(DrawLineV) + REG(DrawCircle) + REG(DrawCircleGradient) + REG(DrawCircleV) + REG(DrawCircleLines) + REG(DrawRectangle) + REG(DrawRectangleRec) + REG(DrawRectangleGradient) + REG(DrawRectangleV) + REG(DrawRectangleLines) + REG(DrawTriangle) + REG(DrawTriangleLines) + REG(DrawPoly) + REG(DrawPolyEx) + REG(DrawPolyExLines) + REG(CheckCollisionRecs) + REG(CheckCollisionCircles) + REG(CheckCollisionCircleRec) + REG(GetCollisionRec) + REG(CheckCollisionPointRec) + REG(CheckCollisionPointCircle) + REG(CheckCollisionPointTriangle) + REG(LoadImage) + REG(LoadImageEx) + REG(LoadImageRaw) + REG(LoadImageFromRES) + REG(LoadTexture) + REG(LoadTextureFromRES) + REG(LoadTextureFromImage) + REG(UnloadImage) + REG(UnloadTexture) + REG(GetImageData) + REG(GetTextureData) + REG(ImageToPOT) + REG(ImageFormat) + REG(GenTextureMipmaps) + REG(DrawTexture) + REG(DrawTextureV) + REG(DrawTextureEx) + REG(DrawTextureRec) + REG(DrawTexturePro) + REG(GetDefaultFont) + REG(LoadSpriteFont) + REG(UnloadSpriteFont) + REG(DrawText) + REG(DrawTextEx) + REG(MeasureText) + REG(MeasureTextEx) + REG(DrawFPS) + REG(DrawCube) + REG(DrawCubeV) + REG(DrawCubeWires) + REG(DrawCubeTexture) + REG(DrawSphere) + REG(DrawSphereEx) + REG(DrawSphereWires) + REG(DrawCylinder) + REG(DrawCylinderWires) + REG(DrawPlane) + REG(DrawRay) + REG(DrawGrid) + REG(DrawGizmo) + REG(LoadModel) + REG(LoadModelEx) + REG(LoadHeightmap) + REG(LoadCubicmap) + REG(UnloadModel) + REG(DrawModel) + REG(DrawModelEx) + REG(DrawModelWires) + REG(DrawBillboard) + REG(DrawBillboardRec) + REG(CheckCollisionSpheres) + REG(CheckCollisionBoxes) + REG(CheckCollisionBoxSphere) + REG(ResolveCollisionCubicmap) + REG(LoadShader) + REG(UnloadShader) + REG(GetShaderLocation) + REG(SetShaderValue) + REG(SetShaderValuei) + + REG(BeginBlendMode) + REG(EndBlendMode) + REG(InitAudioDevice) + REG(CloseAudioDevice) + REG(LoadSound) + REG(LoadSoundFromWave) + REG(LoadSoundFromRES) + REG(UnloadSound) + REG(PlaySound) + REG(PauseSound) + REG(StopSound) + REG(IsSoundPlaying) + REG(SetSoundVolume) + REG(SetSoundPitch) + REG(PlayMusicStream) + REG(UpdateMusicStream) + REG(StopMusicStream) + REG(PauseMusicStream) + REG(ResumeMusicStream) + REG(IsMusicPlaying) + REG(SetMusicVolume) + REG(GetMusicTimeLength) + REG(GetMusicTimePlayed) + + /// Math and util + REG(DecompressData) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) + REG(WriteBitmap) + REG(WritePNG) +#endif + REG(TraceLog) + REG(GetExtension) + REG(GetNextPOT) + REG(VectorAdd) + REG(VectorSubtract) + REG(VectorCrossProduct) + REG(VectorPerpendicular) + REG(VectorDotProduct) + REG(VectorLength) + REG(VectorScale) + REG(VectorNegate) + REG(VectorNormalize) + REG(VectorDistance) + REG(VectorLerp) + REG(VectorReflect) + REG(VectorTransform) + REG(VectorZero) + REG(MatrixDeterminant) + REG(MatrixTrace) + REG(MatrixTranspose) + REG(MatrixInvert) + REG(MatrixNormalize) + REG(MatrixIdentity) + REG(MatrixAdd) + REG(MatrixSubstract) + REG(MatrixTranslate) + REG(MatrixRotate) + REG(MatrixRotateX) + REG(MatrixRotateY) + REG(MatrixRotateZ) + REG(MatrixScale) + REG(MatrixMultiply) + REG(MatrixFrustum) + REG(MatrixPerspective) + REG(MatrixOrtho) + REG(MatrixLookAt) + REG(QuaternionLength) + REG(QuaternionNormalize) + REG(QuaternionMultiply) + REG(QuaternionSlerp) + REG(QuaternionFromMatrix) + REG(QuaternionToMatrix) + REG(QuaternionFromAxisAngle) + REG(QuaternionToAxisAngle) + REG(QuaternionTransform) + + {0,0} +}; + +// Register raylib functionality +static void LuaRegisterRayLib(const char *opt_table) +{ + if (opt_table) lua_createtable(L, 0, sizeof(raylib_functions)/sizeof(raylib_functions[0])); + else lua_pushglobaltable(L); + + luaL_setfuncs(L, raylib_functions, 0); +} + +//---------------------------------------------------------------------------------- +// raylib Lua API +//---------------------------------------------------------------------------------- + +// Initialize Lua system +RLUADEF void InitLuaDevice(void) +{ + mainLuaState = luaL_newstate(); + L = mainLuaState; + + LuaStartEnum(); + LuaSetEnum("FULLSCREEN_MODE", 1); + LuaSetEnum("SHOW_LOGO", 2); + LuaSetEnum("SHOW_MOUSE_CURSOR", 4); + LuaSetEnum("CENTERED_MODE", 8); + LuaSetEnum("MSAA_4X_HINT", 16); + LuaSetEnum("VSYNC_HINT", 32); + LuaEndEnum("FLAG"); + + LuaStartEnum(); + LuaSetEnum("SPACE", 32); + LuaSetEnum("ESCAPE", 256); + LuaSetEnum("ENTER", 257); + LuaSetEnum("BACKSPACE", 259); + LuaSetEnum("RIGHT", 262); + LuaSetEnum("LEFT", 263); + LuaSetEnum("DOWN", 264); + LuaSetEnum("UP", 265); + LuaSetEnum("F1", 290); + LuaSetEnum("F2", 291); + LuaSetEnum("F3", 292); + LuaSetEnum("F4", 293); + LuaSetEnum("F5", 294); + LuaSetEnum("F6", 295); + LuaSetEnum("F7", 296); + LuaSetEnum("F8", 297); + LuaSetEnum("F9", 298); + LuaSetEnum("F10", 299); + LuaSetEnum("LEFT_SHIFT", 340); + LuaSetEnum("LEFT_CONTROL", 341); + LuaSetEnum("LEFT_ALT", 342); + LuaSetEnum("RIGHT_SHIFT", 344); + LuaSetEnum("RIGHT_CONTROL", 345); + LuaSetEnum("RIGHT_ALT", 346); + LuaSetEnum("ZERO", 48); + LuaSetEnum("ONE", 49); + LuaSetEnum("TWO", 50); + LuaSetEnum("THREE", 51); + LuaSetEnum("FOUR", 52); + LuaSetEnum("FIVE", 53); + LuaSetEnum("SIX", 54); + LuaSetEnum("SEVEN", 55); + LuaSetEnum("EIGHT", 56); + LuaSetEnum("NINE", 57); + LuaSetEnum("A", 65); + LuaSetEnum("B", 66); + LuaSetEnum("C", 67); + LuaSetEnum("D", 68); + LuaSetEnum("E", 69); + LuaSetEnum("F", 70); + LuaSetEnum("G", 71); + LuaSetEnum("H", 72); + LuaSetEnum("I", 73); + LuaSetEnum("J", 74); + LuaSetEnum("K", 75); + LuaSetEnum("L", 76); + LuaSetEnum("M", 77); + LuaSetEnum("N", 78); + LuaSetEnum("O", 79); + LuaSetEnum("P", 80); + LuaSetEnum("Q", 81); + LuaSetEnum("R", 82); + LuaSetEnum("S", 83); + LuaSetEnum("T", 84); + LuaSetEnum("U", 85); + LuaSetEnum("V", 86); + LuaSetEnum("W", 87); + LuaSetEnum("X", 88); + LuaSetEnum("Y", 89); + LuaSetEnum("Z", 90); + LuaEndEnum("KEY"); + + LuaStartEnum(); + LuaSetEnum("LEFT_BUTTON", 0); + LuaSetEnum("RIGHT_BUTTON", 1); + LuaSetEnum("MIDDLE_BUTTON", 2); + LuaEndEnum("MOUSE"); + + LuaStartEnum(); + LuaSetEnum("PLAYER1", 0); + LuaSetEnum("PLAYER2", 1); + LuaSetEnum("PLAYER3", 2); + LuaSetEnum("PLAYER4", 3); + + LuaSetEnum("BUTTON_A", 2); + LuaSetEnum("BUTTON_B", 1); + LuaSetEnum("BUTTON_X", 3); + LuaSetEnum("BUTTON_Y", 4); + LuaSetEnum("BUTTON_R1", 7); + LuaSetEnum("BUTTON_R2", 5); + LuaSetEnum("BUTTON_L1", 6); + LuaSetEnum("BUTTON_L2", 8); + LuaSetEnum("BUTTON_SELECT", 9); + LuaSetEnum("BUTTON_START", 10); + LuaEndEnum("GAMEPAD"); + + LuaStartEnum(); + LuaSetEnum("PLAYER1", 0); + LuaSetEnum("PLAYER2", 1); + LuaSetEnum("PLAYER3", 2); + LuaSetEnum("PLAYER4", 3); + + LuaSetEnum("BUTTON_A", 2); + LuaSetEnum("BUTTON_B", 1); + LuaSetEnum("BUTTON_X", 3); + LuaSetEnum("BUTTON_Y", 4); + LuaSetEnum("BUTTON_R1", 7); + LuaSetEnum("BUTTON_R2", 5); + LuaSetEnum("BUTTON_L1", 6); + LuaSetEnum("BUTTON_L2", 8); + LuaSetEnum("BUTTON_SELECT", 9); + LuaSetEnum("BUTTON_START", 10); + LuaEndEnum("GAMEPAD"); + + // TODO: XBOX controller buttons enum + + lua_pushglobaltable(L); + LuaSetEnumColor("LIGHTGRAY", LIGHTGRAY); + LuaSetEnumColor("GRAY", GRAY); + LuaSetEnumColor("DARKGRAY", DARKGRAY); + LuaSetEnumColor("YELLOW", YELLOW); + LuaSetEnumColor("GOLD", GOLD); + LuaSetEnumColor("ORANGE", ORANGE); + LuaSetEnumColor("PINK", PINK); + LuaSetEnumColor("RED", RED); + LuaSetEnumColor("MAROON", MAROON); + LuaSetEnumColor("GREEN", GREEN); + LuaSetEnumColor("LIME", LIME); + LuaSetEnumColor("DARKGREEN", DARKGREEN); + LuaSetEnumColor("SKYBLUE", SKYBLUE); + LuaSetEnumColor("BLUE", BLUE); + LuaSetEnumColor("DARKBLUE", DARKBLUE); + LuaSetEnumColor("PURPLE", PURPLE); + LuaSetEnumColor("VIOLET", VIOLET); + LuaSetEnumColor("DARKPURPLE", DARKPURPLE); + LuaSetEnumColor("BEIGE", BEIGE); + LuaSetEnumColor("BROWN", BROWN); + LuaSetEnumColor("DARKBROWN", DARKBROWN); + LuaSetEnumColor("WHITE", WHITE); + LuaSetEnumColor("BLACK", BLACK); + LuaSetEnumColor("BLANK", BLANK); + LuaSetEnumColor("MAGENTA", MAGENTA); + LuaSetEnumColor("RAYWHITE", RAYWHITE); + lua_pop(L, 1); + + LuaStartEnum(); + LuaSetEnum("UNCOMPRESSED_GRAYSCALE", UNCOMPRESSED_GRAYSCALE); + LuaSetEnum("UNCOMPRESSED_GRAY_ALPHA", UNCOMPRESSED_GRAY_ALPHA); + LuaSetEnum("UNCOMPRESSED_R5G6B5", UNCOMPRESSED_R5G6B5); + LuaSetEnum("UNCOMPRESSED_R8G8B8", UNCOMPRESSED_R8G8B8); + LuaSetEnum("UNCOMPRESSED_R5G5B5A1", UNCOMPRESSED_R5G5B5A1); + LuaSetEnum("UNCOMPRESSED_R4G4B4A4", UNCOMPRESSED_R4G4B4A4); + LuaSetEnum("UNCOMPRESSED_R8G8B8A8", UNCOMPRESSED_R8G8B8A8); + LuaSetEnum("COMPRESSED_DXT1_RGB", COMPRESSED_DXT1_RGB); + LuaSetEnum("COMPRESSED_DXT1_RGBA", COMPRESSED_DXT1_RGBA); + LuaSetEnum("COMPRESSED_DXT3_RGBA", COMPRESSED_DXT3_RGBA); + LuaSetEnum("COMPRESSED_DXT5_RGBA", COMPRESSED_DXT5_RGBA); + LuaSetEnum("COMPRESSED_ETC1_RGB", COMPRESSED_ETC1_RGB); + LuaSetEnum("COMPRESSED_ETC2_RGB", COMPRESSED_ETC2_RGB); + LuaSetEnum("COMPRESSED_ETC2_EAC_RGBA", COMPRESSED_ETC2_EAC_RGBA); + LuaSetEnum("COMPRESSED_PVRT_RGB", COMPRESSED_PVRT_RGB); + LuaSetEnum("COMPRESSED_PVRT_RGBA", COMPRESSED_PVRT_RGBA); + LuaSetEnum("COMPRESSED_ASTC_4x4_RGBA", COMPRESSED_ASTC_4x4_RGBA); + LuaSetEnum("COMPRESSED_ASTC_8x8_RGBA", COMPRESSED_ASTC_8x8_RGBA); + LuaEndEnum("TextureFormat"); + + LuaStartEnum(); + LuaSetEnum("ALPHA", BLEND_ALPHA); + LuaSetEnum("ADDITIVE", BLEND_ADDITIVE); + LuaSetEnum("MULTIPLIED", BLEND_MULTIPLIED); + LuaEndEnum("BlendMode"); + + LuaStartEnum(); + LuaSetEnum("NONE", GESTURE_NONE); + LuaSetEnum("TAP", GESTURE_TAP); + LuaSetEnum("DOUBLETAP", GESTURE_DOUBLETAP); + LuaSetEnum("HOLD", GESTURE_HOLD); + LuaSetEnum("DRAG", GESTURE_DRAG); + LuaSetEnum("SWIPE_RIGHT", GESTURE_SWIPE_RIGHT); + LuaSetEnum("SWIPE_LEFT", GESTURE_SWIPE_LEFT); + LuaSetEnum("SWIPE_UP", GESTURE_SWIPE_UP); + LuaSetEnum("SWIPE_DOWN", GESTURE_SWIPE_DOWN); + LuaSetEnum("PINCH_IN", GESTURE_PINCH_IN); + LuaSetEnum("PINCH_OUT", GESTURE_PINCH_OUT); + LuaEndEnum("Gestures"); + + LuaStartEnum(); + LuaSetEnum("CUSTOM", CAMERA_CUSTOM); + LuaSetEnum("FREE", CAMERA_FREE); + LuaSetEnum("ORBITAL", CAMERA_ORBITAL); + LuaSetEnum("FIRST_PERSON", CAMERA_FIRST_PERSON); + LuaSetEnum("THIRD_PERSON", CAMERA_THIRD_PERSON); + LuaEndEnum("CameraMode"); + + LuaStartEnum(); + LuaSetEnum("HMD_DEFAULT_DEVICE", HMD_DEFAULT_DEVICE); + LuaSetEnum("HMD_OCULUS_RIFT_DK2", HMD_OCULUS_RIFT_DK2); + LuaSetEnum("HMD_OCULUS_RIFT_CV1", HMD_OCULUS_RIFT_CV1); + LuaSetEnum("HMD_VALVE_HTC_VIVE", HMD_VALVE_HTC_VIVE); + LuaSetEnum("HMD_SAMSUNG_GEAR_VR", HMD_SAMSUNG_GEAR_VR); + LuaSetEnum("HMD_GOOGLE_CARDBOARD", HMD_GOOGLE_CARDBOARD); + LuaSetEnum("HMD_SONY_PLAYSTATION_VR", HMD_SONY_PLAYSTATION_VR); + LuaSetEnum("HMD_RAZER_OSVR", HMD_RAZER_OSVR); + LuaSetEnum("HMD_FOVE_VR", HMD_FOVE_VR); + LuaEndEnum("VrDevice"); + + lua_pushglobaltable(L); + LuaSetEnum("INFO", INFO); + LuaSetEnum("ERROR", ERROR); + LuaSetEnum("WARNING", WARNING); + LuaSetEnum("DEBUG", DEBUG); + LuaSetEnum("OTHER", OTHER); + lua_pop(L, 1); + + lua_pushboolean(L, true); +#if defined(PLATFORM_DESKTOP) + lua_setglobal(L, "PLATFORM_DESKTOP"); +#elif defined(PLATFORM_ANDROID) + lua_setglobal(L, "PLATFORM_ANDROID"); +#elif defined(PLATFORM_RPI) + lua_setglobal(L, "PLATFORM_RPI"); +#elif defined(PLATFORM_WEB) + lua_setglobal(L, "PLATFORM_WEB"); +#endif + + luaL_openlibs(L); + LuaBuildOpaqueMetatables(); + + LuaRegisterRayLib(0); +} + +// De-initialize Lua system +RLUADEF void CloseLuaDevice(void) +{ + if (mainLuaState) + { + lua_close(mainLuaState); + mainLuaState = 0; + L = 0; + } +} + +// Execute raylib Lua code +RLUADEF void ExecuteLuaCode(const char *code) +{ + if (!mainLuaState) + { + TraceLog(WARNING, "Lua device not initialized"); + return; + } + + int result = luaL_dostring(L, code); + + switch (result) + { + case LUA_OK: break; + case LUA_ERRRUN: TraceLog(ERROR, "Lua Runtime Error: %s", lua_tostring(L, -1)); break; + case LUA_ERRMEM: TraceLog(ERROR, "Lua Memory Error: %s", lua_tostring(L, -1)); break; + default: TraceLog(ERROR, "Lua Error: %s", lua_tostring(L, -1)); break; + } +} + +// Execute raylib Lua script +RLUADEF void ExecuteLuaFile(const char *filename) +{ + if (!mainLuaState) + { + TraceLog(WARNING, "Lua device not initialized"); + return; + } + + int result = luaL_dofile(L, filename); + + switch (result) + { + case LUA_OK: break; + case LUA_ERRRUN: TraceLog(ERROR, "Lua Runtime Error: %s", lua_tostring(L, -1)); + case LUA_ERRMEM: TraceLog(ERROR, "Lua Memory Error: %s", lua_tostring(L, -1)); + default: TraceLog(ERROR, "Lua Error: %s", lua_tostring(L, -1)); + } +} + +#endif // RLUA_IMPLEMENTATION -- cgit v1.2.3 From 9923fe51a7f69f94c765cfe0dbbf718f287a7a2f Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 12 Sep 2016 19:36:55 +0200 Subject: Tweak to avoid warning --- src/external/jar_mod.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/external') diff --git a/src/external/jar_mod.h b/src/external/jar_mod.h index c39db65a..bee9f6ee 100644 --- a/src/external/jar_mod.h +++ b/src/external/jar_mod.h @@ -59,7 +59,7 @@ // - Initialize the jar_mod_context_t buffer. Must be called before doing anything else. // Return 1 if success. 0 in case of error. // ------------------------------------------- -// mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename) +// mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename) // // - "Load" a MOD from file, context must already be initialized. // Return size of file in bytes. @@ -247,7 +247,7 @@ bool jar_mod_init(jar_mod_context_t * modctx); bool jar_mod_setcfg(jar_mod_context_t * modctx, int samplerate, int bits, int stereo, int stereo_separation, int filter); void jar_mod_fillbuffer(jar_mod_context_t * modctx, short * outbuffer, unsigned long nbsample, jar_mod_tracker_buffer_state * trkbuf); void jar_mod_unload(jar_mod_context_t * modctx); -mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename); +mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename); mulong jar_mod_current_samples(jar_mod_context_t * modctx); mulong jar_mod_max_samples(jar_mod_context_t * modctx); void jar_mod_seek_start(jar_mod_context_t * ctx); @@ -1516,7 +1516,7 @@ void jar_mod_unload( jar_mod_context_t * modctx) -mulong jar_mod_load_file(jar_mod_context_t * modctx, char* filename) +mulong jar_mod_load_file(jar_mod_context_t * modctx, const char* filename) { mulong fsize = 0; if(modctx->modfile) -- cgit v1.2.3 From c5bf9623d155efc94325b1ecc311d66a8ee2a704 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 20 Sep 2016 20:16:19 +0200 Subject: Updated LibOVR to SDK version 1.8 Weird, OVR_Version.h still points to 1.7, probably a typo... --- src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h | 151 +++++++++++++-------- .../OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h | 4 + .../OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h | 3 + .../OculusSDK/LibOVR/Include/OVR_CAPI_GL.h | 3 + .../OculusSDK/LibOVR/Include/OVR_ErrorCode.h | 9 ++ .../OculusSDK/LibOVR/Include/OVR_Version.h | 2 +- src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll | Bin 1023952 -> 1044944 bytes 7 files changed, 114 insertions(+), 58 deletions(-) (limited to 'src/external') diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h index 53340478..eaabcf59 100644 --- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h +++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI.h @@ -271,6 +271,12 @@ typedef char ovrBool; ///< Boolean type //----------------------------------------------------------------------------------- // ***** Simple Math Structures +/// A RGBA color with normalized float components. +typedef struct OVR_ALIGNAS(4) ovrColorf_ +{ + float r, g, b, a; +} ovrColorf; + /// A 2D vector with integer components. typedef struct OVR_ALIGNAS(4) ovrVector2i_ { @@ -326,7 +332,7 @@ typedef struct OVR_ALIGNAS(4) ovrPosef_ /// A full pose (rigid body) configuration with first and second derivatives. /// /// Body refers to any object for which ovrPoseStatef is providing data. -/// It can be the HMD, Touch controller, sensor or something else. The context +/// It can be the HMD, Touch controller, sensor or something else. The context /// depends on the usage of the struct. typedef struct OVR_ALIGNAS(8) ovrPoseStatef_ { @@ -687,7 +693,7 @@ typedef enum ovrTextureFormat_ /// typedef enum ovrTextureMiscFlags_ { - ovrTextureMisc_None, + ovrTextureMisc_None, /// DX only: The underlying texture is created with a TYPELESS equivalent of the /// format specified in the texture desc. The SDK will still access the @@ -745,12 +751,12 @@ typedef struct ovrMirrorTextureData* ovrMirrorTexture; //----------------------------------------------------------------------------------- /// Describes button input types. -/// Button inputs are combined; that is they will be reported as pressed if they are +/// Button inputs are combined; that is they will be reported as pressed if they are /// pressed on either one of the two devices. /// The ovrButton_Up/Down/Left/Right map to both XBox D-Pad and directional buttons. /// The ovrButton_Enter and ovrButton_Return map to Start and Back controller buttons, respectively. typedef enum ovrButton_ -{ +{ ovrButton_A = 0x00000001, ovrButton_B = 0x00000002, ovrButton_RThumb = 0x00000004, @@ -758,7 +764,7 @@ typedef enum ovrButton_ ovrButton_X = 0x00000100, ovrButton_Y = 0x00000200, - ovrButton_LThumb = 0x00000400, + ovrButton_LThumb = 0x00000400, ovrButton_LShoulder = 0x00000800, // Navigation through DPad. @@ -770,7 +776,7 @@ typedef enum ovrButton_ ovrButton_Back = 0x00200000, // Back on Xbox controller. ovrButton_VolUp = 0x00400000, // only supported by Remote. ovrButton_VolDown = 0x00800000, // only supported by Remote. - ovrButton_Home = 0x01000000, + ovrButton_Home = 0x01000000, ovrButton_Private = ovrButton_VolUp | ovrButton_VolDown | ovrButton_Home, // Bit mask of all buttons on the right Touch controller @@ -807,7 +813,7 @@ typedef enum ovrTouch_ // Bit mask of all the button touches on the left controller ovrTouch_LButtonMask = ovrTouch_X | ovrTouch_Y | ovrTouch_LThumb | ovrTouch_LThumbRest | ovrTouch_LIndexTrigger, - // Finger pose state + // Finger pose state // Derived internally based on distance, proximity to sensors and filtering. ovrTouch_RIndexPointing = 0x00000020, ovrTouch_RThumbUp = 0x00000040, @@ -883,11 +889,20 @@ typedef struct ovrHapticsPlaybackState_ int SamplesQueued; } ovrHapticsPlaybackState; +/// Position tracked devices +typedef enum ovrTrackedDeviceType_ +{ + ovrTrackedDevice_HMD = 0x0001, + ovrTrackedDevice_LTouch = 0x0002, + ovrTrackedDevice_RTouch = 0x0004, + ovrTrackedDevice_Touch = 0x0006, + ovrTrackedDevice_All = 0xFFFF, +} ovrTrackedDeviceType; /// Provides names for the left and right hand array indexes. /// /// \see ovrInputState, ovrTrackingState -/// +/// typedef enum ovrHandType_ { ovrHand_Left = 0, @@ -903,27 +918,43 @@ typedef enum ovrHandType_ /// their inputs are combined. typedef struct ovrInputState_ { - // System type when the controller state was last updated. + /// System type when the controller state was last updated. double TimeInSeconds; - // Values for buttons described by ovrButton. + /// Values for buttons described by ovrButton. unsigned int Buttons; - // Touch values for buttons and sensors as described by ovrTouch. + /// Touch values for buttons and sensors as described by ovrTouch. unsigned int Touches; - - // Left and right finger trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f. + + /// Left and right finger trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f. + /// Returns 0 if the value would otherwise be less than 0.1176, for ovrControllerType_XBox float IndexTrigger[ovrHand_Count]; - - // Left and right hand trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f. + + /// Left and right hand trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f. float HandTrigger[ovrHand_Count]; - // Horizontal and vertical thumbstick axis values (ovrHand_Left and ovrHand_Right), in the range -1.0f to 1.0f. + /// Horizontal and vertical thumbstick axis values (ovrHand_Left and ovrHand_Right), in the range -1.0f to 1.0f. + /// Returns a deadzone (value 0) per each axis if the value on that axis would otherwise have been between -.2746 to +.2746, for ovrControllerType_XBox ovrVector2f Thumbstick[ovrHand_Count]; - // The type of the controller this state is for. + /// The type of the controller this state is for. ovrControllerType ControllerType; - + + /// Left and right finger trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f. + /// Does not apply a deadzone + /// Added in 1.7 + float IndexTriggerNoDeadzone[ovrHand_Count]; + + /// Left and right hand trigger values (ovrHand_Left and ovrHand_Right), in the range 0.0 to 1.0f. + /// Does not apply a deadzone + /// Added in 1.7 + float HandTriggerNoDeadzone[ovrHand_Count]; + + /// Horizontal and vertical thumbstick axis values (ovrHand_Left and ovrHand_Right), in the range -1.0f to 1.0f + /// Does not apply a deadzone + /// Added in 1.7 + ovrVector2f ThumbstickNoDeadzone[ovrHand_Count]; } ovrInputState; @@ -996,8 +1027,8 @@ typedef struct OVR_ALIGNAS(8) ovrInitParams_ /// Use NULL to specify no log callback. ovrLogCallback LogCallback; - /// User-supplied data which is passed as-is to LogCallback. Typically this - /// is used to store an application-specific pointer which is read in the + /// User-supplied data which is passed as-is to LogCallback. Typically this + /// is used to store an application-specific pointer which is read in the /// callback function. uintptr_t UserData; @@ -1014,6 +1045,7 @@ typedef struct OVR_ALIGNAS(8) ovrInitParams_ extern "C" { #endif +#if !defined(OVR_EXPORTING_CAPI) // ----------------------------------------------------------------------------------- // ***** API Interfaces @@ -1026,7 +1058,7 @@ extern "C" { /// followed by a call to ovr_Shutdown. ovr_Initialize calls are idempotent. /// Calling ovr_Initialize twice does not require two matching calls to ovr_Shutdown. /// If already initialized, the return value is ovr_Success. -/// +/// /// LibOVRRT shared library search order: /// -# Current working directory (often the same as the application directory). /// -# Module directory (usually the same as the application directory, @@ -1166,21 +1198,21 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_IdentifyClient(const char* identity); /// /// ovr_Initialize must have first been called in order for this to succeed, otherwise ovrHmdDesc::Type /// will be reported as ovrHmd_None. -/// +/// /// \param[in] session Specifies an ovrSession previously returned by ovr_Create, else NULL in which /// case this function detects whether an HMD is present and returns its info if so. /// -/// \return Returns an ovrHmdDesc. If the hmd is NULL and ovrHmdDesc::Type is ovrHmd_None then +/// \return Returns an ovrHmdDesc. If the hmd is NULL and ovrHmdDesc::Type is ovrHmd_None then /// no HMD is present. /// OVR_PUBLIC_FUNCTION(ovrHmdDesc) ovr_GetHmdDesc(ovrSession session); -/// Returns the number of sensors. +/// Returns the number of sensors. /// -/// The number of sensors may change at any time, so this function should be called before use +/// The number of sensors may change at any time, so this function should be called before use /// as opposed to once on startup. -/// +/// /// \param[in] session Specifies an ovrSession previously returned by ovr_Create. /// /// \return Returns unsigned int count. @@ -1190,15 +1222,15 @@ OVR_PUBLIC_FUNCTION(unsigned int) ovr_GetTrackerCount(ovrSession session); /// Returns a given sensor description. /// -/// It's possible that sensor desc [0] may indicate a unconnnected or non-pose tracked sensor, but +/// It's possible that sensor desc [0] may indicate a unconnnected or non-pose tracked sensor, but /// sensor desc [1] may be connected. /// /// ovr_Initialize must have first been called in order for this to succeed, otherwise the returned /// trackerDescArray will be zero-initialized. The data returned by this function can change at runtime. -/// +/// /// \param[in] session Specifies an ovrSession previously returned by ovr_Create. -/// -/// \param[in] trackerDescIndex Specifies a sensor index. The valid indexes are in the range of 0 to +/// +/// \param[in] trackerDescIndex Specifies a sensor index. The valid indexes are in the range of 0 to /// the sensor count returned by ovr_GetTrackerCount. /// /// \return Returns ovrTrackerDesc. An empty ovrTrackerDesc will be returned if trackerDescIndex is out of range. @@ -1242,6 +1274,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_Create(ovrSession* pSession, ovrGraphicsLuid* /// OVR_PUBLIC_FUNCTION(void) ovr_Destroy(ovrSession session); +#endif // !defined(OVR_EXPORTING_CAPI) /// Specifies status information for the current session. /// @@ -1253,10 +1286,11 @@ typedef struct ovrSessionStatus_ ovrBool HmdPresent; ///< True if an HMD is present. ovrBool HmdMounted; ///< True if the HMD is on the user's head. ovrBool DisplayLost; ///< True if the session is in a display-lost state. See ovr_SubmitFrame. - ovrBool ShouldQuit; ///< True if the application should initiate shutdown. + ovrBool ShouldQuit; ///< True if the application should initiate shutdown. ovrBool ShouldRecenter; ///< True if UX has requested re-centering. Must call ovr_ClearShouldRecenterFlag or ovr_RecenterTrackingOrigin. }ovrSessionStatus; +#if !defined(OVR_EXPORTING_CAPI) /// Returns status information for the application. /// @@ -1293,7 +1327,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetSessionStatus(ovrSession session, ovrSessi /// /// When the tracking origin is changed, all of the calls that either provide /// or accept ovrPosef will use the new tracking origin provided. -/// +/// /// \param[in] session Specifies an ovrSession previously returned by ovr_Create. /// \param[in] origin Specifies an ovrTrackingOrigin to be used for all ovrPosef /// @@ -1305,7 +1339,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SetTrackingOriginType(ovrSession session, ovr /// Gets the tracking origin state -/// +/// /// \param[in] session Specifies an ovrSession previously returned by ovr_Create. /// /// \return Returns the ovrTrackingOrigin that was either set by default, or previous set by the application. @@ -1319,9 +1353,9 @@ OVR_PUBLIC_FUNCTION(ovrTrackingOrigin) ovr_GetTrackingOriginType(ovrSession sess /// This resets the (x,y,z) positional components and the yaw orientation component. /// The Roll and pitch orientation components are always determined by gravity and cannot /// be redefined. All future tracking will report values relative to this new reference position. -/// If you are using ovrTrackerPoses then you will need to call ovr_GetTrackerPose after +/// If you are using ovrTrackerPoses then you will need to call ovr_GetTrackerPose after /// this, because the sensor position(s) will change as a result of this. -/// +/// /// The headset cannot be facing vertically upward or downward but rather must be roughly /// level otherwise this function will fail with ovrError_InvalidHeadsetOrientation. /// @@ -1343,7 +1377,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_RecenterTrackingOrigin(ovrSession session); /// Clears the ShouldRecenter status bit in ovrSessionStatus. /// -/// Clears the ShouldRecenter status bit in ovrSessionStatus, allowing further recenter +/// Clears the ShouldRecenter status bit in ovrSessionStatus, allowing further recenter /// requests to be detected. Since this is automatically done by ovr_RecenterTrackingOrigin, /// this is only needs to be called when application is doing its own re-centering. OVR_PUBLIC_FUNCTION(void) ovr_ClearShouldRecenterFlag(ovrSession session); @@ -1444,11 +1478,11 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SubmitControllerVibration(ovrSession session, /// \param[in] outState State of the haptics engine. /// \return Returns ovrSuccess upon success. /// \see ovrHapticsPlaybackState -/// +/// OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetControllerVibrationState(ovrSession session, ovrControllerType controllerType, ovrHapticsPlaybackState* outState); -///@} +#endif // !defined(OVR_EXPORTING_CAPI) //------------------------------------------------------------------------------------- // @name Layers @@ -1672,7 +1706,7 @@ typedef union ovrLayer_Union_ //@} - +#if !defined(OVR_EXPORTING_CAPI) /// @name SDK Distortion Rendering /// @@ -1695,7 +1729,7 @@ typedef union ovrLayer_Union_ /// \param[in] chain Specifies the ovrTextureSwapChain for which the length should be retrieved. /// \param[out] out_Length Returns the number of buffers in the specified chain. /// -/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. +/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. /// /// \see ovr_CreateTextureSwapChainDX, ovr_CreateTextureSwapChainGL /// @@ -1707,7 +1741,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetTextureSwapChainLength(ovrSession session, /// \param[in] chain Specifies the ovrTextureSwapChain for which the index should be retrieved. /// \param[out] out_Index Returns the current (free) index in specified chain. /// -/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. +/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. /// /// \see ovr_CreateTextureSwapChainDX, ovr_CreateTextureSwapChainGL /// @@ -1719,7 +1753,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetTextureSwapChainCurrentIndex(ovrSession se /// \param[in] chain Specifies the ovrTextureSwapChain for which the description should be retrieved. /// \param[out] out_Desc Returns the description of the specified chain. /// -/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. +/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. /// /// \see ovr_CreateTextureSwapChainDX, ovr_CreateTextureSwapChainGL /// @@ -1736,7 +1770,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetTextureSwapChainDesc(ovrSession session, o /// it will synchronize with the app's graphics context and pick up the submitted index, opening up /// room in the swap chain for further commits. /// -/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. +/// \return Returns an ovrResult for which OVR_SUCCESS(result) is false upon error. /// Failures include but aren't limited to: /// - ovrError_TextureSwapChainFull: ovr_CommitTextureSwapChain was called too many times on a texture swapchain without calling submit to use the chain. /// @@ -1864,7 +1898,7 @@ OVR_PUBLIC_FUNCTION(ovrEyeRenderDesc) ovr_GetRenderDesc(ovrSession session, /// destroyed (ovr_Destroy) and recreated (ovr_Create), and new resources need to be created /// (ovr_CreateTextureSwapChainXXX). The application's existing private graphics resources do not /// need to be recreated unless the new ovr_Create call returns a different GraphicsLuid. -/// - ovrError_TextureSwapChainInvalid: The ovrTextureSwapChain is in an incomplete or inconsistent state. +/// - ovrError_TextureSwapChainInvalid: The ovrTextureSwapChain is in an incomplete or inconsistent state. /// Ensure ovr_CommitTextureSwapChain was called at least once first. /// /// \see ovr_GetPredictedDisplayTime, ovrViewScaleDesc, ovrLayerHeader @@ -1874,7 +1908,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SubmitFrame(ovrSession session, long long fra ovrLayerHeader const * const * layerPtrList, unsigned int layerCount); ///@} - +#endif // !defined(OVR_EXPORTING_CAPI) //------------------------------------------------------------------------------------- /// @name Frame Timing @@ -1882,26 +1916,28 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_SubmitFrame(ovrSession session, long long fra //@{ +#if !defined(OVR_EXPORTING_CAPI) + /// Gets the time of the specified frame midpoint. /// -/// Predicts the time at which the given frame will be displayed. The predicted time -/// is the middle of the time period during which the corresponding eye images will -/// be displayed. +/// Predicts the time at which the given frame will be displayed. The predicted time +/// is the middle of the time period during which the corresponding eye images will +/// be displayed. /// /// The application should increment frameIndex for each successively targeted frame, -/// and pass that index to any relevent OVR functions that need to apply to the frame +/// and pass that index to any relevant OVR functions that need to apply to the frame /// identified by that index. /// /// This function is thread-safe and allows for multiple application threads to target /// their processing to the same displayed frame. -/// +/// /// In the even that prediction fails due to various reasons (e.g. the display being off /// or app has yet to present any frames), the return value will be current CPU time. -/// +/// /// \param[in] session Specifies an ovrSession previously returned by ovr_Create. /// \param[in] frameIndex Identifies the frame the caller wishes to target. /// A value of zero returns the next frame index. -/// \return Returns the absolute frame midpoint time for the given frameIndex. +/// \return Returns the absolute frame midpoint time for the given frameIndex. /// \see ovr_GetTimeInSeconds /// OVR_PUBLIC_FUNCTION(double) ovr_GetPredictedDisplayTime(ovrSession session, long long frameIndex); @@ -1917,6 +1953,7 @@ OVR_PUBLIC_FUNCTION(double) ovr_GetPredictedDisplayTime(ovrSession session, long /// OVR_PUBLIC_FUNCTION(double) ovr_GetTimeInSeconds(); +#endif // !defined(OVR_EXPORTING_CAPI) /// Performance HUD enables the HMD user to see information critical to /// the real-time operation of the VR application such as latency timing, @@ -1958,7 +1995,7 @@ typedef enum ovrLayerHudMode_ ///@} /// Debug HUD is provided to help developers gauge and debug the fidelity of their app's -/// stereo rendering characteristics. Using the provided quad and crosshair guides, +/// stereo rendering characteristics. Using the provided quad and crosshair guides, /// the developer can verify various aspects such as VR tracking units (e.g. meters), /// stereo camera-parallax properties (e.g. making sure objects at infinity are rendered /// with the proper separation), measuring VR geometry sizes and distances and more. @@ -1984,7 +2021,7 @@ typedef enum ovrDebugHudStereoMode_ } ovrDebugHudStereoMode; - +#if !defined(OVR_EXPORTING_CAPI) // ----------------------------------------------------------------------------------- /// @name Property Access @@ -2102,7 +2139,7 @@ OVR_PUBLIC_FUNCTION(ovrBool) ovr_SetString(ovrSession session, const char* prope ///@} - +#endif // !defined(OVR_EXPORTING_CAPI) #ifdef __cplusplus } // extern "C" @@ -2163,10 +2200,10 @@ OVR_STATIC_ASSERT(sizeof(ovrLogLevel) == 4, "ovrLogLevel size mismatch"); OVR_STATIC_ASSERT(sizeof(ovrInitParams) == 4 + 4 + sizeof(ovrLogCallback) + sizeof(uintptr_t) + 4 + 4, "ovrInitParams size mismatch"); -OVR_STATIC_ASSERT(sizeof(ovrHmdDesc) == +OVR_STATIC_ASSERT(sizeof(ovrHmdDesc) == + sizeof(ovrHmdType) // Type OVR_ON64(+ 4) // pad0 - + 64 // ProductName + + 64 // ProductName + 64 // Manufacturer + 2 // VendorId + 2 // ProductId diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h index 930dfcbe..dc61e19e 100644 --- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h +++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_Audio.h @@ -17,6 +17,8 @@ #include "OVR_CAPI.h" #define OVR_AUDIO_MAX_DEVICE_STR_SIZE 128 +#if !defined(OVR_EXPORTING_CAPI) + /// Gets the ID of the preferred VR audio output device. /// /// \param[out] deviceOutId The ID of the user's preferred VR audio device to use, which will be valid upon a successful return value, else it will be WAVE_MAPPER. @@ -75,6 +77,8 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetAudioDeviceInGuidStr(WCHAR deviceInStrBuff /// OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetAudioDeviceInGuid(GUID* deviceInGuid); +#endif // !defined(OVR_EXPORTING_CAPI) + #endif //OVR_OS_MS #endif // OVR_CAPI_Audio_h diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h index 982af8f0..374dab84 100644 --- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h +++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_D3D.h @@ -14,6 +14,8 @@ #if defined (_WIN32) #include +#if !defined(OVR_EXPORTING_CAPI) + //----------------------------------------------------------------------------------- // ***** Direct3D Specific @@ -149,6 +151,7 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetMirrorTextureBufferDX(ovrSession session, IID iid, void** out_Buffer); +#endif // !defined(OVR_EXPORTING_CAPI) #endif // _WIN32 diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h index 81487947..1c073f46 100644 --- a/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h +++ b/src/external/OculusSDK/LibOVR/Include/OVR_CAPI_GL.h @@ -9,6 +9,8 @@ #include "OVR_CAPI.h" +#if !defined(OVR_EXPORTING_CAPI) + /// Creates a TextureSwapChain suitable for use with OpenGL. /// /// \param[in] session Specifies an ovrSession previously returned by ovr_Create. @@ -95,5 +97,6 @@ OVR_PUBLIC_FUNCTION(ovrResult) ovr_GetMirrorTextureBufferGL(ovrSession session, ovrMirrorTexture mirrorTexture, unsigned int* out_TexId); +#endif // !defined(OVR_EXPORTING_CAPI) #endif // OVR_CAPI_GL_h diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h b/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h index 9fc527c7..a8b810ea 100644 --- a/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h +++ b/src/external/OculusSDK/LibOVR/Include/OVR_ErrorCode.h @@ -88,6 +88,7 @@ typedef enum ovrErrorType_ ovrError_ClientSkippedDestroy = -1012, ///< The client failed to call ovr_Destroy on an active session before calling ovr_Shutdown. Or the client crashed. ovrError_ClientSkippedShutdown = -1013, ///< The client failed to call ovr_Shutdown or the client crashed. ovrError_ServiceDeadlockDetected = -1014, ///< The service watchdog discovered a deadlock. + ovrError_InvalidOperation = -1015, ///< Function call is invalid for object's current state /* Audio error range, reserved for Audio errors. */ ovrError_AudioDeviceNotFound = -2001, ///< Failure to find the specified audio device. @@ -115,6 +116,9 @@ typedef enum ovrErrorType_ ovrError_HybridGraphicsNotSupported = -3018, ///< The system is using hybrid graphics (Optimus, etc...), which is not support. ovrError_DisplayManagerInit = -3019, ///< Initialization of the DisplayManager failed. ovrError_TrackerDriverInit = -3020, ///< Failed to get the interface for an attached tracker + ovrError_LibSignCheck = -3021, ///< LibOVRRT signature check failure. + ovrError_LibPath = -3022, ///< LibOVRRT path failure. + ovrError_LibSymbols = -3023, ///< LibOVRRT symbol resolution failure. /* Rendering errors */ ovrError_DisplayLost = -6000, ///< In the event of a system-wide graphics reset or cable unplug this is returned to the app. @@ -130,6 +134,11 @@ typedef enum ovrErrorType_ /* Fatal errors */ ovrError_RuntimeException = -7000, ///< A runtime exception occurred. The application is required to shutdown LibOVR and re-initialize it before this error state will be cleared. + /* Calibration errors */ + ovrError_NoCalibration = -9000, ///< Result of a missing calibration block + ovrError_OldVersion = -9001, ///< Result of an old calibration block + ovrError_MisformattedBlock = -9002, ///< Result of a bad calibration block due to lengths + } ovrErrorType; diff --git a/src/external/OculusSDK/LibOVR/Include/OVR_Version.h b/src/external/OculusSDK/LibOVR/Include/OVR_Version.h index d8378304..8d213df5 100644 --- a/src/external/OculusSDK/LibOVR/Include/OVR_Version.h +++ b/src/external/OculusSDK/LibOVR/Include/OVR_Version.h @@ -19,7 +19,7 @@ // Master version numbers #define OVR_PRODUCT_VERSION 1 // Product version doesn't participate in semantic versioning. #define OVR_MAJOR_VERSION 1 // If you change these values then you need to also make sure to change LibOVR/Projects/Windows/LibOVR.props in parallel. -#define OVR_MINOR_VERSION 6 // +#define OVR_MINOR_VERSION 7 // #define OVR_PATCH_VERSION 0 #define OVR_BUILD_NUMBER 0 diff --git a/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll b/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll index 843c0e44..7a42d443 100644 Binary files a/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll and b/src/external/OculusSDK/LibOVR/LibOVRRT32_1.dll differ -- cgit v1.2.3 From b1651baea504411dc47ef257094a089c9107dd73 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Mon, 10 Oct 2016 18:22:55 +0200 Subject: Added support for FLAC audio loading/streaming --- src/audio.c | 62 +- src/external/dr_flac.h | 4395 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 4455 insertions(+), 2 deletions(-) create mode 100644 src/external/dr_flac.h (limited to 'src/external') diff --git a/src/audio.c b/src/audio.c index 90bf4968..bf66f929 100644 --- a/src/audio.c +++ b/src/audio.c @@ -79,6 +79,10 @@ #define JAR_MOD_IMPLEMENTATION #include "external/jar_mod.h" // MOD loading functions +#define DR_FLAC_IMPLEMENTATION +#define DR_FLAC_NO_WIN32_IO +#include "external/dr_flac.h" // FLAC loading functions + #ifdef _MSC_VER #undef bool #endif @@ -98,12 +102,13 @@ // Types and Structures Definition //---------------------------------------------------------------------------------- -typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_MODULE_XM, MUSIC_MODULE_MOD } MusicContextType; +typedef enum { MUSIC_AUDIO_OGG = 0, MUSIC_AUDIO_FLAC, MUSIC_MODULE_XM, MUSIC_MODULE_MOD } MusicContextType; // Music type (file streaming from memory) typedef struct MusicData { MusicContextType ctxType; // Type of music context (OGG, XM, MOD) stb_vorbis *ctxOgg; // OGG audio context + drflac *ctxFlac; // FLAC audio context jar_xm_context_t *ctxXm; // XM chiptune context jar_mod_context_t ctxMod; // MOD chiptune context @@ -128,6 +133,7 @@ typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; //---------------------------------------------------------------------------------- static Wave LoadWAV(const char *fileName); // Load WAV file static Wave LoadOGG(const char *fileName); // Load OGG file +static Wave LoadFLAC(const char *fileName); // Load FLAC file #if defined(AUDIO_STANDALONE) const char *GetExtension(const char *fileName); // Get the extension for a filename @@ -212,6 +218,7 @@ Wave LoadWave(const char *fileName) if (strcmp(GetExtension(fileName), "wav") == 0) wave = LoadWAV(fileName); else if (strcmp(GetExtension(fileName), "ogg") == 0) wave = LoadOGG(fileName); + else if (strcmp(GetExtension(fileName), "flac") == 0) wave = LoadFLAC(fileName); else TraceLog(WARNING, "[%s] File extension not recognized, it can't be loaded", fileName); return wave; @@ -672,7 +679,7 @@ Music LoadMusicStream(const char *fileName) // Open ogg audio stream music->ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL); - if (music->ctxOgg == NULL) TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName); + if (music->ctxOgg == NULL) TraceLog(WARNING, "[%s] OGG audio file could not be opened", fileName); else { stb_vorbis_info info = stb_vorbis_get_info(music->ctxOgg); // Get Ogg file info @@ -691,6 +698,24 @@ Music LoadMusicStream(const char *fileName) } } + else if (strcmp(GetExtension(fileName), "flac") == 0) + { + music->ctxFlac = drflac_open_file(fileName); + + if (music->ctxFlac == NULL) TraceLog(WARNING, "[%s] FLAC audio file could not be opened", fileName); + else + { + music->stream = InitAudioStream(music->ctxFlac->sampleRate, music->ctxFlac->bitsPerSample, music->ctxFlac->channels); + music->totalSamples = music->ctxFlac->totalSampleCount; + music->samplesLeft = music->totalSamples; + music->ctxType = MUSIC_AUDIO_FLAC; + music->loop = true; // We loop by default + + TraceLog(DEBUG, "[%s] FLAC sample rate: %i", fileName, music->ctxFlac->sampleRate); + TraceLog(DEBUG, "[%s] FLAC bits per sample: %i", fileName, music->ctxFlac->bitsPerSample); + TraceLog(DEBUG, "[%s] FLAC channels: %i", fileName, music->ctxFlac->channels); + } + } else if (strcmp(GetExtension(fileName), "xm") == 0) { int result = jar_xm_create_context_from_file(&music->ctxXm, 48000, fileName); @@ -739,6 +764,7 @@ void UnloadMusicStream(Music music) CloseAudioStream(music->stream); if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg); + else if (music->ctxType == MUSIC_AUDIO_FLAC) drflac_free(music->ctxFlac); else if (music->ctxType == MUSIC_MODULE_XM) jar_xm_free_context(music->ctxXm); else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod); @@ -818,6 +844,20 @@ void UpdateMusicStream(Music music) UpdateAudioStream(music->stream, pcm, numSamplesOgg*music->stream.channels); music->samplesLeft -= (numSamplesOgg*music->stream.channels); + } break; + case MUSIC_AUDIO_FLAC: + { + if (music->samplesLeft >= AUDIO_BUFFER_SIZE) numSamples = AUDIO_BUFFER_SIZE; + else numSamples = music->samplesLeft; + + int pcmi[AUDIO_BUFFER_SIZE]; + + // NOTE: Returns the number of samples to process (should be the same as numSamples) + int numSamplesFlac = drflac_read_s32(music->ctxFlac, numSamples, pcmi); + + UpdateAudioStream(music->stream, pcmi, numSamples*music->stream.channels); + music->samplesLeft -= (numSamples*music->stream.channels); + } break; case MUSIC_MODULE_XM: { @@ -1214,6 +1254,24 @@ static Wave LoadOGG(const char *fileName) return wave; } +// Load FLAC file into Wave structure +// NOTE: Using dr_flac library +static Wave LoadFLAC(const char *fileName) +{ + Wave wave; + + // Decode an entire FLAC file in one go + uint64_t totalSampleCount; + wave.data = drflac_open_and_decode_file_s32(fileName, &wave.channels, &wave.sampleRate, &totalSampleCount); + + wave.sampleCount = (int)totalSampleCount; + wave.sampleSize = 32; + + if (wave.data == NULL) TraceLog(WARNING, "[%s] FLAC data could not be loaded", fileName); + + return wave; +} + // Some required functions for audio standalone module version #if defined(AUDIO_STANDALONE) // Get the extension for a filename diff --git a/src/external/dr_flac.h b/src/external/dr_flac.h new file mode 100644 index 00000000..d7b66f20 --- /dev/null +++ b/src/external/dr_flac.h @@ -0,0 +1,4395 @@ +// FLAC audio decoder. Public domain. See "unlicense" statement at the end of this file. +// dr_flac - v0.4 - 2016-09-29 +// +// David Reid - mackron@gmail.com + +// USAGE +// +// dr_flac is a single-file library. To use it, do something like the following in one .c file. +// #define DR_FLAC_IMPLEMENTATION +// #include "dr_flac.h" +// +// You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, +// do something like the following: +// +// drflac* pFlac = drflac_open_file("MySong.flac"); +// if (pFlac == NULL) { +// // Failed to open FLAC file +// } +// +// int32_t* pSamples = malloc(pFlac->totalSampleCount * sizeof(int32_t)); +// uint64_t numberOfInterleavedSamplesActuallyRead = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSamples); +// +// The drflac object represents the decoder. It is a transparent type so all the information you need, such as the number of +// channels and the bits per sample, should be directly accessible - just make sure you don't change their values. Samples are +// always output as interleaved signed 32-bit PCM. In the example above a native FLAC stream was opened, however dr_flac has +// seamless support for Ogg encapsulated FLAC streams as well. +// +// You do not need to decode the entire stream in one go - you just specify how many samples you'd like at any given time and +// the decoder will give you as many samples as it can, up to the amount requested. Later on when you need the next batch of +// samples, just call it again. Example: +// +// while (drflac_read_s32(pFlac, chunkSize, pChunkSamples) > 0) { +// do_something(); +// } +// +// You can seek to a specific sample with drflac_seek_to_sample(). The given sample is based on interleaving. So for example, +// if you were to seek to the sample at index 0 in a stereo stream, you'll be seeking to the first sample of the left channel. +// The sample at index 1 will be the first sample of the right channel. The sample at index 2 will be the second sample of the +// left channel, etc. +// +// +// If you just want to quickly decode an entire FLAC file in one go you can do something like this: +// +// unsigned int channels; +// unsigned int sampleRate; +// uint64_t totalSampleCount; +// int32_t* pSampleData = drflac_open_and_decode_file("MySong.flac", &channels, &sampleRate, &totalSampleCount); +// if (pSampleData == NULL) { +// // Failed to open and decode FLAC file. +// } +// +// ... +// +// drflac_free(pSampleData); +// +// +// 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. See https://github.com/mackron/dr_libs_tests/blob/master/dr_flac/dr_flac_test_2.c for +// an example on how to read metadata. +// +// +// +// OPTIONS +// #define these options before including this file. +// +// #define DR_FLAC_NO_STDIO +// Disable drflac_open_file(). +// +// #define DR_FLAC_NO_OGG +// Disables support for Ogg/FLAC streams. +// +// #define DR_FLAC_NO_WIN32_IO +// In the Win32 build, dr_flac uses the Win32 IO APIs for drflac_open_file() by default. This setting will make it use the +// standard FILE APIs instead. Ignored when DR_FLAC_NO_STDIO is #defined. (The rationale for this configuration is that +// there's a bug in one compiler's Win32 implementation of the FILE APIs which is not present in the Win32 IO APIs.) +// +// #define DR_FLAC_BUFFER_SIZE +// Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls +// back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing +// returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of +// onRead(), or increase it if it's very inefficient. Must be a multiple of 8. +// +// +// +// QUICK NOTES +// - Based on my tests, the performance of the 32-bit build is at about parity with the reference implementation. The 64-bit build +// is slightly faster. +// - dr_flac does not currently do any CRC checks. +// - dr_flac should work fine with valid native FLAC files, but for broadcast streams it won't work if the header and STREAMINFO +// block is unavailable. +// - Audio data is output as signed 32-bit PCM, regardless of the bits per sample the FLAC stream is encoded as. +// - This has not been tested on big-endian architectures. +// - Rice codes in unencoded binary form (see https://xiph.org/flac/format.html#rice_partition) has not been tested. If anybody +// knows where I can find some test files for this, let me know. +// - Perverse and erroneous files have not been tested. Again, if you know where I can get some test files let me know. +// - dr_flac is not thread-safe, but it's APIs can be called from any thread so long as you do your own synchronization. + +#ifndef dr_flac_h +#define dr_flac_h + +#include +#include + +#ifndef DR_BOOL_DEFINED +#define DR_BOOL_DEFINED +#ifdef _WIN32 +typedef char drBool8; +typedef int drBool32; +#else +#include +typedef int8_t drBool8; +typedef int32_t drBool32; +#endif +#define DR_TRUE 1 +#define DR_FALSE 0 +#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) +#define DRFLAC_64BIT +#endif + +#if defined(__GNUC__) +#if defined(__x86_64__) || defined(__ppc64__) +#define DRFLAC_64BIT +#endif +#endif + +#ifdef DRFLAC_64BIT +typedef uint64_t drflac_cache_t; +#else +typedef uint32_t 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; + +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 +{ + uint64_t firstSample; + uint64_t frameOffset; // The offset from the first byte of the header of the first frame. + uint16_t sampleCount; +} drflac_seekpoint; +#pragma pack() + +typedef struct +{ + uint16_t minBlockSize; + uint16_t maxBlockSize; + uint32_t minFrameSize; + uint32_t maxFrameSize; + uint32_t sampleRate; + uint8_t channels; + uint8_t bitsPerSample; + uint64_t totalSampleCount; + uint8_t md5[16]; +} drflac_streaminfo; + +typedef struct +{ + // The metadata type. Use this to know how to interpret the data below. + uint32_t 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. + uint32_t rawDataSize; + + union + { + drflac_streaminfo streaminfo; + + struct + { + int unused; + } padding; + + struct + { + uint32_t id; + const void* pData; + uint32_t dataSize; + } application; + + struct + { + uint32_t seekpointCount; + const drflac_seekpoint* pSeekpoints; + } seektable; + + struct + { + uint32_t vendorLength; + const char* vendor; + uint32_t commentCount; + const char* comments; + } vorbis_comment; + + struct + { + char catalog[128]; + uint64_t leadInSampleCount; + drBool32 isCD; + uint8_t trackCount; + const uint8_t* pTrackData; + } cuesheet; + + struct + { + uint32_t type; + uint32_t mimeLength; + const char* mime; + uint32_t descriptionLength; + const char* description; + uint32_t width; + uint32_t height; + uint32_t colorDepth; + uint32_t indexColorCount; + uint32_t pictureDataSize; + const uint8_t* 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. +typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); + +// Callback for when data needs to be seeked. +// +// pUserData [in] The user data that was passed to drflac_open() and family. +// offset [in] The number of bytes to move, relative to the origin. Will never be negative. +// origin [in] The origin of the seek - the current position or the start of the stream. +// +// Returns whether or not the seek was successful. +// +// The offset will never be negative. Whether or not it is relative to the beginning or current position is determined +// by the "origin" parameter which will be either drflac_seek_origin_start or drflac_seek_origin_current. +typedef drBool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); + +// Callback for when a metadata block is read. +// +// pUserData [in] The user data that was passed to drflac_open() and family. +// pMetadata [in] A pointer to a structure containing the data of the metadata block. +// +// Use pMetadata->type to determine which metadata block is being handled and how to read the data. +typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); + + +// Structure for internal use. Only used for decoders opened with drflac_open_memory. +typedef struct +{ + const uint8_t* 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. + size_t nextL2Line; + + // The number of bits that have been consumed by the cache. This is used to determine how many valid bits are remaining. + size_t 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; + +} drflac_bs; + +typedef struct +{ + // The type of the subframe: SUBFRAME_CONSTANT, SUBFRAME_VERBATIM, SUBFRAME_FIXED or SUBFRAME_LPC. + uint8_t subframeType; + + // The number of wasted bits per sample as specified by the sub-frame header. + uint8_t wastedBitsPerSample; + + // The order to use for the prediction stage for SUBFRAME_FIXED and SUBFRAME_LPC. + uint8_t lpcOrder; + + // The number of bits per sample for this subframe. This is not always equal to the current frame's bit per sample because + // an extra bit is required for side channels when interchannel decorrelation is being used. + uint32_t bitsPerSample; + + // A pointer to the buffer containing the decoded samples in the subframe. This pointer is an offset from drflac::pExtraData, or + // NULL if the heap is not being used. Note that it's a signed 32-bit integer for each value. + int32_t* pDecodedSamples; + +} drflac_subframe; + +typedef struct +{ + // If the stream uses variable block sizes, this will be set to the index of the first sample. If fixed block sizes are used, this will + // always be set to 0. + uint64_t sampleNumber; + + // If the stream uses fixed block sizes, this will be set to the frame number. If variable block sizes are used, this will always be 0. + uint32_t frameNumber; + + // The sample rate of this frame. + uint32_t sampleRate; + + // The number of samples in each sub-frame within this frame. + uint16_t blockSize; + + // The channel assignment of this frame. This is not always set to the channel count. If interchannel decorrelation is being used this + // will be set to DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE, DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE or DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE. + uint8_t channelAssignment; + + // The number of bits per sample within this frame. + uint8_t bitsPerSample; + + // The frame's CRC. This is set, but unused at the moment. + uint8_t crc8; + +} drflac_frame_header; + +typedef struct +{ + // The header. + drflac_frame_header header; + + // The number of samples left to be read in this frame. This is initially set to the block size multiplied by the channel count. As samples + // are read, this will be decremented. When it reaches 0, the decoder will see this frame as fully consumed and load the next frame. + uint32_t samplesRemaining; + + // The list of sub-frames within the frame. There is one sub-frame for each channel, and there's a maximum of 8 channels. + drflac_subframe subframes[8]; + +} drflac_frame; + +typedef struct +{ + // The function to call when a metadata block is read. + drflac_meta_proc onMeta; + + // The user data posted to the metadata callback function. + void* pUserDataMD; + + + // The sample rate. Will be set to something like 44100. + uint32_t 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. + uint8_t channels; + + // The bits per sample. Will be set to somthing like 16, 24, etc. + uint8_t bitsPerSample; + + // The maximum block size, in samples. This number represents the number of samples in each channel (not combined). + uint16_t maxBlockSize; + + // The total number of samples making up the stream. This includes every channel. For example, if the stream has 2 channels, + // with each channel having a total of 4096, this value will be set to 2*4096 = 8192. Can be 0 in which case it's still a + // valid stream, but just means the total sample count is unknown. Likely the case with streams like internet radio. + uint64_t totalSampleCount; + + + // The container type. This is set based on whether or not the decoder was opened from a native or Ogg stream. + drflac_container container; + + + // The position of the seektable in the file. + uint64_t seektablePos; + + // The size of the seektable. + uint32_t seektableSize; + + + // Information about the frame the decoder is currently sitting on. + drflac_frame currentFrame; + + // The position of the first frame in the stream. This is only ever used for seeking. + uint64_t firstFramePos; + + + // A hack to avoid a malloc() when opening a decoder with drflac_open_memory(). + drflac__memory_stream memoryStream; + + + + // A pointer to the decoded sample data. This is an offset of pExtraData. + int32_t* pDecodedSamples; + + + // 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 avoid unnecessary mallocs. + uint8_t pExtraData[1]; + +} drflac; + + +// Opens a FLAC decoder. +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This function will automatically detect whether or not you are attempting to open a native or Ogg encapsulated +// FLAC, both of which should work seamlessly without any manual intervention. Ogg encapsulation also works with +// multiplexed streams which basically means it can play FLAC encoded audio tracks in videos. +// +// This is the lowest level function for opening a FLAC stream. You can also use drflac_open_file() and drflac_open_memory() +// to open the stream from a file or from a block of memory respectively. +// +// The STREAMINFO block must be present for this to succeed. +// +// 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); + +// Opens a FLAC decoder and notifies the caller of the metadata chunks (album art, etc.). +// +// onRead [in] The function to call when data needs to be read from the client. +// onSeek [in] The function to call when the read position of the client data needs to move. +// onMeta [in] The function to call for every metadata block. +// pUserData [in, optional] A pointer to application defined data that will be passed to onRead, onSeek and onMeta. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This is slower than drflac_open(), so avoid this one if you don't need metadata. Internally, this will do a malloc() +// and free() for every metadata block except for STREAMINFO and PADDING blocks. +// +// The caller is notified of the metadata via the onMeta callback. All metadata blocks with be handled before the function +// returns. +// +// 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); + +// Closes the given FLAC decoder. +// +// pFlac [in] The decoder to close. +// +// This will destroy the decoder object. +void drflac_close(drflac* pFlac); + + +// Reads sample data from the given FLAC decoder, output as interleaved signed 32-bit PCM. +// +// pFlac [in] The decoder. +// samplesToRead [in] The number of samples to read. +// pBufferOut [out, optional] A pointer to the buffer that will receive the decoded samples. +// +// Returns the number of samples actually read. +// +// pBufferOut can be null, in which case the call will act as a seek, and the return value will be the number of samples +// seeked. +uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* pBufferOut); + +// Seeks to the sample at the given index. +// +// pFlac [in] The decoder. +// sampleIndex [in] The index of the sample to seek to. See notes below. +// +// Returns DR_TRUE if successful; DR_FALSE otherwise. +// +// The sample index is based on interleaving. In a stereo stream, for example, the sample at index 0 is the first sample +// in the left channel; the sample at index 1 is the first sample on the right channel, and so on. +// +// When seeking, you will likely want to ensure it's rounded to a multiple of the channel count. You can do this with +// something like drflac_seek_to_sample(pFlac, (mySampleIndex + (mySampleIndex % pFlac->channels))) +drBool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex); + + + +#ifndef DR_FLAC_NO_STDIO +// Opens a FLAC decoder from the file at the given path. +// +// filename [in] The path of the file to open, either absolute or relative to the current directory. +// +// Returns a pointer to an object representing the decoder. +// +// Close the decoder with drflac_close(). +// +// This will hold a handle to the file until the decoder is closed with drflac_close(). Some platforms will restrict the +// number of files a process can have open at any given time, so keep this mind if you have many decoders open at the +// same time. +// +// See also: drflac_open(), drflac_open_file_with_metadata(), drflac_close() +drflac* drflac_open_file(const char* filename); + +// Opens a FLAC decoder from the file at the given path and notifies the caller of the metadata chunks (album art, etc.) +// +// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData); +#endif + +// Opens a FLAC decoder from a pre-allocated block of memory +// +// This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for +// the lifetime of the decoder. +drflac* drflac_open_memory(const void* data, size_t dataSize); + +// Opens a FLAC decoder from a pre-allocated block of memory and notifies the caller of the metadata chunks (album art, etc.) +// +// Look at the documentation for drflac_open_with_metadata() for more information on how metadata is handled. +drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData); + + + +//// High Level APIs //// + +// Opens a FLAC stream from the given callbacks and fully decodes it in a single operation. The return value is a +// pointer to the sample data as interleaved signed 32-bit PCM. The returned data must be freed with drflac_free(). +// +// Sometimes a FLAC file won't keep track of the total sample count. In this situation the function will continuously +// read samples into a dynamically sized buffer on the heap until no samples are left. +// +// Do not call this function on a broadcast type of stream (like internet radio streams and whatnot). +int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); + +#ifndef DR_FLAC_NO_STDIO +// Same as drflac_open_and_decode_s32() except opens the decoder from a file. +int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); +#endif + +// Same as drflac_open_and_decode_s32() except opens the decoder from a block of memory. +int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); + +// Frees data returned by drflac_open_and_decode_*(). +void drflac_free(void* pSampleDataReturnedByOpenAndDecode); + + +// Structure representing an iterator for vorbis comments in a VORBIS_COMMENT metadata block. +typedef struct +{ + uint32_t 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, uint32_t commentCount, const char* pComments); + +// Goes to the next vorbis comment in the given iterator. If null is returned it means there are no more comments. The +// returned string is NOT null terminated. +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, uint32_t* pCommentLengthOut); + + + +#ifdef __cplusplus +} +#endif +#endif //dr_flac_h + + +/////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +/////////////////////////////////////////////////////////////////////////////// +#ifdef DR_FLAC_IMPLEMENTATION +#include +#include +#include + +#ifdef _MSC_VER +#include // For _byteswap_ulong and _byteswap_uint64 +#endif + +#ifdef __linux__ +#define _BSD_SOURCE +#include +#endif + +#ifdef _MSC_VER +#define DRFLAC_INLINE __forceinline +#else +#define DRFLAC_INLINE inline +#endif + +#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 + + +//// Endian Management //// +static DRFLAC_INLINE drBool32 drflac__is_little_endian() +{ + int n = 1; + return (*(char*)&n) == 1; +} + +static DRFLAC_INLINE uint16_t drflac__swap_endian_uint16(uint16_t n) +{ +#ifdef _MSC_VER + return _byteswap_ushort(n); +#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + return __builtin_bswap16(n); +#else + return ((n & 0xFF00) >> 8) | + ((n & 0x00FF) << 8); +#endif +} + +static DRFLAC_INLINE uint32_t drflac__swap_endian_uint32(uint32_t n) +{ +#ifdef _MSC_VER + return _byteswap_ulong(n); +#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + return __builtin_bswap32(n); +#else + return ((n & 0xFF000000) >> 24) | + ((n & 0x00FF0000) >> 8) | + ((n & 0x0000FF00) << 8) | + ((n & 0x000000FF) << 24); +#endif +} + +static DRFLAC_INLINE uint64_t drflac__swap_endian_uint64(uint64_t n) +{ +#ifdef _MSC_VER + return _byteswap_uint64(n); +#elif defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + return __builtin_bswap64(n); +#else + return ((n & 0xFF00000000000000ULL) >> 56) | + ((n & 0x00FF000000000000ULL) >> 40) | + ((n & 0x0000FF0000000000ULL) >> 24) | + ((n & 0x000000FF00000000ULL) >> 8) | + ((n & 0x00000000FF000000ULL) << 8) | + ((n & 0x0000000000FF0000ULL) << 24) | + ((n & 0x000000000000FF00ULL) << 40) | + ((n & 0x00000000000000FFULL) << 56); +#endif +} + +static DRFLAC_INLINE uint16_t drflac__be2host_16(uint16_t n) +{ +#ifdef __linux__ + return be16toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint16(n); + } + + return n; +#endif +} + +static DRFLAC_INLINE uint32_t drflac__be2host_32(uint32_t n) +{ +#ifdef __linux__ + return be32toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +#endif +} + +static DRFLAC_INLINE uint64_t drflac__be2host_64(uint64_t n) +{ +#ifdef __linux__ + return be64toh(n); +#else + if (drflac__is_little_endian()) { + return drflac__swap_endian_uint64(n); + } + + return n; +#endif +} + + +static DRFLAC_INLINE uint32_t drflac__le2host_32(uint32_t n) +{ +#ifdef __linux__ + return le32toh(n); +#else + if (!drflac__is_little_endian()) { + return drflac__swap_endian_uint32(n); + } + + return n; +#endif +} + + +#ifdef DRFLAC_64BIT +#define drflac__be2host__cache_line drflac__be2host_64 +#else +#define drflac__be2host__cache_line drflac__be2host_32 +#endif + + +// BIT READING ATTEMPT #2 +// +// This uses a 32- or 64-bit bit-shifted cache - as bits are read, the cache is shifted such that the first valid bit is sitting +// on the most significant bit. It uses the notion of an L1 and L2 cache (borrowed from CPU architecture), where the L1 cache +// is a 32- or 64-bit unsigned integer (depending on whether or not a 32- or 64-bit build is being compiled) and the L2 is an +// array of "cache lines", with each cache line being the same size as the L1. The L2 is a buffer of about 4KB and is where data +// from onRead() is read into. +#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - ((bs)->consumedBits)) +#ifdef DRFLAC_64BIT +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((uint64_t)-1LL) >> (_bitCount))) +#else +#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~(((uint32_t)-1) >> (_bitCount))) +#endif +#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), _bitCount) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), _bitCount)) +#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) + +static DRFLAC_INLINE drBool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) +{ + // Fast path. Try loading straight from L2. + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DR_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 DR_FALSE; // If we have any unaligned bytes it means there's not more aligned bytes left in the client. + } + + size_t bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + + bs->nextL2Line = 0; + if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DR_TRUE; + } + + + // If we get here it means we were unable to retrieve enough data to fill the entire L2 cache. It probably + // means we've just reached the end of the file. We need to move the valid data down to the end of the buffer + // and adjust the index of the next line accordingly. Also keep in mind that the L2 cache must be aligned to + // the size of the L1 so we'll need to seek backwards by any misaligned bytes. + size_t alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + + // We need to keep track of any unaligned bytes for later use. + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + if (bs->unalignedByteCount > 0) { + bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; + } + + if (alignedL1LineCount > 0) + { + size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + for (size_t i = alignedL1LineCount; i > 0; --i) { + bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; + } + + bs->nextL2Line = offset; + bs->cache = bs->cacheL2[bs->nextL2Line++]; + return DR_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 DR_FALSE; + } +} + +static drBool32 drflac__reload_cache(drflac_bs* bs) +{ + // 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; + return DR_TRUE; + } + + // Slow path. + + // If we get here it means we have failed to load the L1 cache from the L2. Likely we've just reached the end of the stream and the last + // few bytes did not meet the alignment requirements for the L2 cache. In this case we need to fall back to a slower path and read the + // data from the unaligned cache. + size_t bytesRead = bs->unalignedByteCount; + if (bytesRead == 0) { + return DR_FALSE; + } + + assert(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + + bs->cache = drflac__be2host__cache_line(bs->unalignedCache); + bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs->consumedBits); // <-- Make sure the consumed bits are always set to zero. Other parts of the library depend on this property. + return DR_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; +} + +static drBool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +{ + if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += bitsToSeek; + bs->cache <<= bitsToSeek; + return DR_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; + + size_t wholeBytesRemaining = bitsToSeek/8; + if (wholeBytesRemaining > 0) + { + // The next bytes to seek will be located in the L2 cache. The problem is that the L2 cache is not byte aligned, + // but rather DRFLAC_CACHE_L1_SIZE_BYTES aligned (usually 4 or 8). If, for example, the number of bytes to seek is + // 3, we'll need to handle it in a special way. + size_t wholeCacheLinesRemaining = wholeBytesRemaining / DRFLAC_CACHE_L1_SIZE_BYTES(bs); + if (wholeCacheLinesRemaining < DRFLAC_CACHE_L2_LINES_REMAINING(bs)) + { + wholeBytesRemaining -= wholeCacheLinesRemaining * DRFLAC_CACHE_L1_SIZE_BYTES(bs); + bitsToSeek -= wholeCacheLinesRemaining * DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->nextL2Line += wholeCacheLinesRemaining; + } + else + { + wholeBytesRemaining -= DRFLAC_CACHE_L2_LINES_REMAINING(bs) * DRFLAC_CACHE_L1_SIZE_BYTES(bs); + bitsToSeek -= DRFLAC_CACHE_L2_LINES_REMAINING(bs) * DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->nextL2Line += DRFLAC_CACHE_L2_LINES_REMAINING(bs); + + if (wholeBytesRemaining > 0) { + bs->onSeek(bs->pUserData, (int)wholeBytesRemaining, drflac_seek_origin_current); + bitsToSeek -= wholeBytesRemaining*8; + } + } + } + + + if (bitsToSeek > 0) { + if (!drflac__reload_cache(bs)) { + return DR_FALSE; + } + + return drflac__seek_bits(bs, bitsToSeek); + } + + return DR_TRUE; + } +} + +static drBool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, uint32_t* pResultOut) +{ + assert(bs != NULL); + assert(pResultOut != NULL); + assert(bitCount > 0); + assert(bitCount <= 32); + + if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!drflac__reload_cache(bs)) { + return DR_FALSE; + } + } + + if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { + if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + bs->consumedBits += bitCount; + bs->cache <<= bitCount; + } else { + *pResultOut = (uint32_t)bs->cache; + bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->cache = 0; + } + return DR_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. + size_t bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); + size_t bitCountLo = bitCount - bitCountHi; + uint32_t resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + + if (!drflac__reload_cache(bs)) { + return DR_FALSE; + } + + *pResultOut = (resultHi << bitCountLo) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + bs->consumedBits += bitCountLo; + bs->cache <<= bitCountLo; + return DR_TRUE; + } +} + +static drBool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, int32_t* pResult) +{ + assert(bs != NULL); + assert(pResult != NULL); + assert(bitCount > 0); + assert(bitCount <= 32); + + uint32_t result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DR_FALSE; + } + + uint32_t signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResult = (int32_t)result; + return DR_TRUE; +} + +static drBool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, uint64_t* pResultOut) +{ + assert(bitCount <= 64); + assert(bitCount > 32); + + uint32_t resultHi; + if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { + return DR_FALSE; + } + + uint32_t resultLo; + if (!drflac__read_uint32(bs, 32, &resultLo)) { + return DR_FALSE; + } + + *pResultOut = (((uint64_t)resultHi) << 32) | ((uint64_t)resultLo); + return DR_TRUE; +} + +// Function below is unused, but leaving it here in case I need to quickly add it again. +#if 0 +static drBool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, int64_t* pResultOut) +{ + assert(bitCount <= 64); + + uint64_t result; + if (!drflac__read_uint64(bs, bitCount, &result)) { + return DR_FALSE; + } + + uint64_t signbit = ((result >> (bitCount-1)) & 0x01); + result |= (~signbit + 1) << bitCount; + + *pResultOut = (int64_t)result; + return DR_TRUE; +} +#endif + +static drBool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, uint16_t* pResult) +{ + assert(bs != NULL); + assert(pResult != NULL); + assert(bitCount > 0); + assert(bitCount <= 16); + + uint32_t result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DR_FALSE; + } + + *pResult = (uint16_t)result; + return DR_TRUE; +} + +static drBool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, int16_t* pResult) +{ + assert(bs != NULL); + assert(pResult != NULL); + assert(bitCount > 0); + assert(bitCount <= 16); + + int32_t result; + if (!drflac__read_int32(bs, bitCount, &result)) { + return DR_FALSE; + } + + *pResult = (int16_t)result; + return DR_TRUE; +} + +static drBool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, uint8_t* pResult) +{ + assert(bs != NULL); + assert(pResult != NULL); + assert(bitCount > 0); + assert(bitCount <= 8); + + uint32_t result; + if (!drflac__read_uint32(bs, bitCount, &result)) { + return DR_FALSE; + } + + *pResult = (uint8_t)result; + return DR_TRUE; +} + +static drBool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, int8_t* pResult) +{ + assert(bs != NULL); + assert(pResult != NULL); + assert(bitCount > 0); + assert(bitCount <= 8); + + int32_t result; + if (!drflac__read_int32(bs, bitCount, &result)) { + return DR_FALSE; + } + + *pResult = (int8_t)result; + return DR_TRUE; +} + + +static inline drBool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +{ + unsigned int zeroCounter = 0; + while (bs->cache == 0) { + zeroCounter += (unsigned int)DRFLAC_CACHE_L1_BITS_REMAINING(bs); + if (!drflac__reload_cache(bs)) { + return DR_FALSE; + } + } + + // At this point the cache should not be zero, in which case we know the first set bit should be somewhere in here. There is + // no need for us to perform any cache reloading logic here which should make things much faster. + assert(bs->cache != 0); + + unsigned int bitOffsetTable[] = { + 0, + 4, + 3, 3, + 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1 + }; + + unsigned int setBitOffsetPlus1 = bitOffsetTable[DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, 4)]; + if (setBitOffsetPlus1 == 0) { + if (bs->cache == 1) { + setBitOffsetPlus1 = DRFLAC_CACHE_L1_SIZE_BITS(bs); + } else { + setBitOffsetPlus1 = 5; + for (;;) + { + if ((bs->cache & DRFLAC_CACHE_L1_SELECT(bs, setBitOffsetPlus1))) { + break; + } + + setBitOffsetPlus1 += 1; + } + } + } + + bs->consumedBits += setBitOffsetPlus1; + bs->cache <<= setBitOffsetPlus1; + + *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; + return DR_TRUE; +} + + + +static drBool32 drflac__seek_to_byte(drflac_bs* bs, uint64_t offsetFromStart) +{ + assert(bs != NULL); + 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) + { + uint64_t bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DR_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + + + while (bytesRemaining > 0x7FFFFFFF) { + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { + return DR_FALSE; + } + bytesRemaining -= 0x7FFFFFFF; + } + + + if (bytesRemaining > 0) { + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { + return DR_FALSE; + } + } + } + else + { + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { + return DR_FALSE; + } + } + + + // The cache should be reset to force a reload of fresh data from the client. + drflac__reset_cache(bs); + return DR_TRUE; +} + + +static drBool32 drflac__read_utf8_coded_number(drflac_bs* bs, uint64_t* pNumberOut) +{ + assert(bs != NULL); + assert(pNumberOut != NULL); + + unsigned char utf8[7] = {0}; + if (!drflac__read_uint8(bs, 8, utf8)) { + *pNumberOut = 0; + return DR_FALSE; + } + + if ((utf8[0] & 0x80) == 0) { + *pNumberOut = utf8[0]; + return DR_TRUE; + } + + int byteCount = 1; + if ((utf8[0] & 0xE0) == 0xC0) { + byteCount = 2; + } else if ((utf8[0] & 0xF0) == 0xE0) { + byteCount = 3; + } else if ((utf8[0] & 0xF8) == 0xF0) { + byteCount = 4; + } else if ((utf8[0] & 0xFC) == 0xF8) { + byteCount = 5; + } else if ((utf8[0] & 0xFE) == 0xFC) { + byteCount = 6; + } else if ((utf8[0] & 0xFF) == 0xFE) { + byteCount = 7; + } else { + *pNumberOut = 0; + return DR_FALSE; // Bad UTF-8 encoding. + } + + // Read extra bytes. + assert(byteCount > 1); + + uint64_t result = (uint64_t)(utf8[0] & (0xFF >> (byteCount + 1))); + for (int i = 1; i < byteCount; ++i) { + if (!drflac__read_uint8(bs, 8, utf8 + i)) { + *pNumberOut = 0; + return DR_FALSE; + } + + result = (result << 6) | (utf8[i] & 0x3F); + } + + *pNumberOut = result; + return DR_TRUE; +} + + + +static DRFLAC_INLINE drBool32 drflac__read_and_seek_rice(drflac_bs* bs, uint8_t m) +{ + unsigned int unused; + if (!drflac__seek_past_next_set_bit(bs, &unused)) { + return DR_FALSE; + } + + if (m > 0) { + if (!drflac__seek_bits(bs, m)) { + return DR_FALSE; + } + } + + return DR_TRUE; +} + + +// 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 int32_t drflac__calculate_prediction_32(uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples) +{ + assert(order <= 32); + + // 32-bit version. + + // VC++ optimizes this to a single jmp. I've not yet verified this for other compilers. + int32_t prediction = 0; + + switch (order) + { + case 32: prediction += coefficients[31] * pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; + } + + return (int32_t)(prediction >> shift); +} + +static DRFLAC_INLINE int32_t drflac__calculate_prediction_64(uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples) +{ + 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 + int64_t prediction; + if (order == 8) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; + prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; + } + else if (order == 7) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; + } + else if (order == 3) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + } + else if (order == 6) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + } + else if (order == 5) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + } + else if (order == 4) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + } + else if (order == 12) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; + prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; + prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; + prediction += coefficients[9] * (int64_t)pDecodedSamples[-10]; + prediction += coefficients[10] * (int64_t)pDecodedSamples[-11]; + prediction += coefficients[11] * (int64_t)pDecodedSamples[-12]; + } + else if (order == 2) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + } + else if (order == 1) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + } + else if (order == 10) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; + prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; + prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; + prediction += coefficients[9] * (int64_t)pDecodedSamples[-10]; + } + else if (order == 9) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; + prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; + prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; + } + else if (order == 11) + { + prediction = coefficients[0] * (int64_t)pDecodedSamples[-1]; + prediction += coefficients[1] * (int64_t)pDecodedSamples[-2]; + prediction += coefficients[2] * (int64_t)pDecodedSamples[-3]; + prediction += coefficients[3] * (int64_t)pDecodedSamples[-4]; + prediction += coefficients[4] * (int64_t)pDecodedSamples[-5]; + prediction += coefficients[5] * (int64_t)pDecodedSamples[-6]; + prediction += coefficients[6] * (int64_t)pDecodedSamples[-7]; + prediction += coefficients[7] * (int64_t)pDecodedSamples[-8]; + prediction += coefficients[8] * (int64_t)pDecodedSamples[-9]; + prediction += coefficients[9] * (int64_t)pDecodedSamples[-10]; + prediction += coefficients[10] * (int64_t)pDecodedSamples[-11]; + } + else + { + prediction = 0; + for (int j = 0; j < (int)order; ++j) { + prediction += coefficients[j] * (int64_t)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 + int64_t prediction = 0; + + switch (order) + { + case 32: prediction += coefficients[31] * (int64_t)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (int64_t)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (int64_t)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (int64_t)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (int64_t)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (int64_t)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (int64_t)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (int64_t)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (int64_t)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (int64_t)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (int64_t)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (int64_t)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (int64_t)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (int64_t)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (int64_t)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (int64_t)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (int64_t)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (int64_t)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (int64_t)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (int64_t)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (int64_t)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (int64_t)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (int64_t)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (int64_t)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (int64_t)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (int64_t)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (int64_t)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (int64_t)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (int64_t)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (int64_t)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (int64_t)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (int64_t)pDecodedSamples[- 1]; + } +#endif + + return (int32_t)(prediction >> shift); +} + + +// Reads and decodes a string of residual values as Rice codes. The decoder should be sitting on the first bit of the Rice codes. +// +// This is the most frequently called function in the library. It does both the Rice decoding and the prediction in a single loop +// iteration. The prediction is done at the end, and there's an annoying branch I'd like to avoid so the main function is defined +// as a #define - sue me! +#define DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(funcName, predictionFunc) \ +static drBool32 funcName (drflac_bs* bs, uint32_t count, uint8_t riceParam, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pSamplesOut) \ +{ \ + assert(bs != NULL); \ + assert(count > 0); \ + assert(pSamplesOut != NULL); \ + \ + static unsigned int bitOffsetTable[] = { \ + 0, \ + 4, \ + 3, 3, \ + 2, 2, 2, 2, \ + 1, 1, 1, 1, 1, 1, 1, 1 \ + }; \ + \ + drflac_cache_t riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); \ + drflac_cache_t resultHiShift = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParam; \ + \ + for (int i = 0; i < (int)count; ++i) \ + { \ + unsigned int zeroCounter = 0; \ + while (bs->cache == 0) { \ + zeroCounter += (unsigned int)DRFLAC_CACHE_L1_BITS_REMAINING(bs); \ + if (!drflac__reload_cache(bs)) { \ + return DR_FALSE; \ + } \ + } \ + \ + /* At this point the cache should not be zero, in which case we know the first set bit should be somewhere in here. There is \ + no need for us to perform any cache reloading logic here which should make things much faster. */ \ + assert(bs->cache != 0); \ + unsigned int decodedRice; \ + \ + unsigned int setBitOffsetPlus1 = bitOffsetTable[DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, 4)]; \ + if (setBitOffsetPlus1 > 0) { \ + decodedRice = (zeroCounter + (setBitOffsetPlus1-1)) << riceParam; \ + } else { \ + if (bs->cache == 1) { \ + setBitOffsetPlus1 = DRFLAC_CACHE_L1_SIZE_BITS(bs); \ + decodedRice = (zeroCounter + (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1)) << riceParam; \ + } else { \ + setBitOffsetPlus1 = 5; \ + for (;;) \ + { \ + if ((bs->cache & DRFLAC_CACHE_L1_SELECT(bs, setBitOffsetPlus1))) { \ + decodedRice = (zeroCounter + (setBitOffsetPlus1-1)) << riceParam; \ + break; \ + } \ + \ + setBitOffsetPlus1 += 1; \ + } \ + } \ + } \ + \ + \ + unsigned int bitsLo = 0; \ + unsigned int riceLength = setBitOffsetPlus1 + riceParam; \ + if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) \ + { \ + bitsLo = (unsigned int)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> (DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceLength)); \ + \ + bs->consumedBits += riceLength; \ + bs->cache <<= riceLength; \ + } \ + else \ + { \ + bs->consumedBits += riceLength; \ + 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. */ \ + size_t bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); \ + drflac_cache_t resultHi = bs->cache & riceParamMask; /* <-- This mask is OK because all bits after the first bits are always zero. */ \ + \ + \ + if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { \ + bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); \ + } else { \ + /* Slow path. We need to fetch more data from the client. */ \ + if (!drflac__reload_cache(bs)) { \ + return DR_FALSE; \ + } \ + } \ + \ + bitsLo = (unsigned int)((resultHi >> resultHiShift) | DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo)); \ + bs->consumedBits = bitCountLo; \ + bs->cache <<= bitCountLo; \ + } \ + \ + decodedRice |= bitsLo; \ + decodedRice = (decodedRice >> 1) ^ (~(decodedRice & 0x01) + 1); /* <-- Ah, much faster! :) */ \ + /* \ + if ((decodedRice & 0x01)) { \ + decodedRice = ~(decodedRice >> 1); \ + } else { \ + decodedRice = (decodedRice >> 1); \ + } \ + */ \ + \ + /* In order to properly calculate the prediction when the bits per sample is >16 we need to do it using 64-bit arithmetic. We can assume this \ + is probably going to be slower on 32-bit systems so we'll do a more optimized 32-bit version when the bits per sample is low enough.*/ \ + pSamplesOut[i] = ((int)decodedRice + predictionFunc(order, shift, coefficients, pSamplesOut + i)); \ + } \ + \ + return DR_TRUE; \ +} \ + +DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(drflac__decode_samples_with_residual__rice_64, drflac__calculate_prediction_64) +DRFLAC__DECODE_SAMPLES_WITH_RESIDULE__RICE__PROC(drflac__decode_samples_with_residual__rice_32, drflac__calculate_prediction_32) + + +// 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 drBool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, uint32_t count, uint8_t riceParam) +{ + assert(bs != NULL); + assert(count > 0); + + for (uint32_t i = 0; i < count; ++i) { + if (!drflac__read_and_seek_rice(bs, riceParam)) { + return DR_FALSE; + } + } + + return DR_TRUE; +} + +static drBool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, uint32_t bitsPerSample, uint32_t count, uint8_t unencodedBitsPerSample, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pSamplesOut) +{ + assert(bs != NULL); + assert(count > 0); + assert(unencodedBitsPerSample > 0 && unencodedBitsPerSample <= 32); + assert(pSamplesOut != NULL); + + for (unsigned int i = 0; i < count; ++i) + { + if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return DR_FALSE; + } + + if (bitsPerSample > 16) { + pSamplesOut[i] += drflac__calculate_prediction_64(order, shift, coefficients, pSamplesOut + i); + } else { + pSamplesOut[i] += drflac__calculate_prediction_32(order, shift, coefficients, pSamplesOut + i); + } + } + + return DR_TRUE; +} + + +// Reads and decodes the residual for the sub-frame the decoder is currently sitting on. This function should be called +// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be ignored. The +// and parameters are used to determine how many residual values need to be decoded. +static drBool32 drflac__decode_samples_with_residual(drflac_bs* bs, uint32_t bitsPerSample, uint32_t blockSize, uint32_t order, int32_t shift, const int16_t* coefficients, int32_t* pDecodedSamples) +{ + assert(bs != NULL); + assert(blockSize != 0); + assert(pDecodedSamples != NULL); // <-- Should we allow NULL, in which case we just seek past the residual rather than do a full decode? + + uint8_t residualMethod; + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DR_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DR_FALSE; // Unknown or unsupported residual coding method. + } + + // Ignore the first values. + pDecodedSamples += order; + + + uint8_t partitionOrder; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DR_FALSE; + } + + + uint32_t samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + uint32_t partitionsRemaining = (1 << partitionOrder); + for (;;) + { + uint8_t riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DR_FALSE; + } + if (riceParam == 16) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DR_FALSE; + } + if (riceParam == 32) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (bitsPerSample > 16) { + if (!drflac__decode_samples_with_residual__rice_64(bs, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { + return DR_FALSE; + } + } else { + if (!drflac__decode_samples_with_residual__rice_32(bs, samplesInPartition, riceParam, order, shift, coefficients, pDecodedSamples)) { + return DR_FALSE; + } + } + } else { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DR_FALSE; + } + + if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, order, shift, coefficients, pDecodedSamples)) { + return DR_FALSE; + } + } + + pDecodedSamples += samplesInPartition; + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + + return DR_TRUE; +} + +// Reads and seeks past the residual for the sub-frame the decoder is currently sitting on. This function should be called +// when the decoder is sitting at the very start of the RESIDUAL block. The first residuals will be set to 0. The +// and parameters are used to determine how many residual values need to be decoded. +static drBool32 drflac__read_and_seek_residual(drflac_bs* bs, uint32_t blockSize, uint32_t order) +{ + assert(bs != NULL); + assert(blockSize != 0); + + uint8_t residualMethod; + if (!drflac__read_uint8(bs, 2, &residualMethod)) { + return DR_FALSE; + } + + if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return DR_FALSE; // Unknown or unsupported residual coding method. + } + + uint8_t partitionOrder; + if (!drflac__read_uint8(bs, 4, &partitionOrder)) { + return DR_FALSE; + } + + uint32_t samplesInPartition = (blockSize / (1 << partitionOrder)) - order; + uint32_t partitionsRemaining = (1 << partitionOrder); + for (;;) + { + uint8_t riceParam = 0; + if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!drflac__read_uint8(bs, 4, &riceParam)) { + return DR_FALSE; + } + if (riceParam == 16) { + riceParam = 0xFF; + } + } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!drflac__read_uint8(bs, 5, &riceParam)) { + return DR_FALSE; + } + if (riceParam == 32) { + riceParam = 0xFF; + } + } + + if (riceParam != 0xFF) { + if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return DR_FALSE; + } + } else { + unsigned char unencodedBitsPerSample = 0; + if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return DR_FALSE; + } + + if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return DR_FALSE; + } + } + + + if (partitionsRemaining == 1) { + break; + } + + partitionsRemaining -= 1; + samplesInPartition = blockSize / (1 << partitionOrder); + } + + return DR_TRUE; +} + + +static drBool32 drflac__decode_samples__constant(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples) +{ + // Only a single sample needs to be decoded here. + int32_t sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DR_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 (uint32_t i = 0; i < blockSize; ++i) { + pDecodedSamples[i] = sample; + } + + return DR_TRUE; +} + +static drBool32 drflac__decode_samples__verbatim(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, int32_t* pDecodedSamples) +{ + for (uint32_t i = 0; i < blockSize; ++i) { + int32_t sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DR_FALSE; + } + + pDecodedSamples[i] = sample; + } + + return DR_TRUE; +} + +static drBool32 drflac__decode_samples__fixed(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples) +{ + short 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 (uint32_t i = 0; i < lpcOrder; ++i) { + int32_t sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DR_FALSE; + } + + pDecodedSamples[i] = sample; + } + + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, 0, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return DR_FALSE; + } + + return DR_TRUE; +} + +static drBool32 drflac__decode_samples__lpc(drflac_bs* bs, uint32_t blockSize, uint32_t bitsPerSample, uint8_t lpcOrder, int32_t* pDecodedSamples) +{ + // Warm up samples. + for (uint8_t i = 0; i < lpcOrder; ++i) { + int32_t sample; + if (!drflac__read_int32(bs, bitsPerSample, &sample)) { + return DR_FALSE; + } + + pDecodedSamples[i] = sample; + } + + uint8_t lpcPrecision; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DR_FALSE; + } + if (lpcPrecision == 15) { + return DR_FALSE; // Invalid. + } + lpcPrecision += 1; + + + int8_t lpcShift; + if (!drflac__read_int8(bs, 5, &lpcShift)) { + return DR_FALSE; + } + + + int16_t coefficients[32]; + for (uint8_t i = 0; i < lpcOrder; ++i) { + if (!drflac__read_int16(bs, lpcPrecision, coefficients + i)) { + return DR_FALSE; + } + } + + if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, coefficients, pDecodedSamples)) { + return DR_FALSE; + } + + return DR_TRUE; +} + + +static drBool32 drflac__read_next_frame_header(drflac_bs* bs, uint8_t streaminfoBitsPerSample, drflac_frame_header* header) +{ + assert(bs != NULL); + assert(header != NULL); + + // At the moment the sync code is as a form of basic validation. The CRC is stored, but is unused at the moment. This + // should probably be handled better in the future. + + const uint32_t sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const uint8_t bitsPerSampleTable[8] = {0, 8, 12, (uint8_t)-1, 16, 20, 24, (uint8_t)-1}; // -1 = reserved. + + uint16_t syncCode = 0; + if (!drflac__read_uint16(bs, 14, &syncCode)) { + return DR_FALSE; + } + + if (syncCode != 0x3FFE) { + // TODO: Try and recover by attempting to seek to and read the next frame? + return DR_FALSE; + } + + uint8_t reserved; + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DR_FALSE; + } + + uint8_t blockingStrategy = 0; + if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { + return DR_FALSE; + } + + + + uint8_t blockSize = 0; + if (!drflac__read_uint8(bs, 4, &blockSize)) { + return DR_FALSE; + } + + uint8_t sampleRate = 0; + if (!drflac__read_uint8(bs, 4, &sampleRate)) { + return DR_FALSE; + } + + uint8_t channelAssignment = 0; + if (!drflac__read_uint8(bs, 4, &channelAssignment)) { + return DR_FALSE; + } + + uint8_t bitsPerSample = 0; + if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { + return DR_FALSE; + } + + if (!drflac__read_uint8(bs, 1, &reserved)) { + return DR_FALSE; + } + + + drBool32 isVariableBlockSize = blockingStrategy == 1; + if (isVariableBlockSize) { + uint64_t sampleNumber; + if (!drflac__read_utf8_coded_number(bs, &sampleNumber)) { + return DR_FALSE; + } + header->frameNumber = 0; + header->sampleNumber = sampleNumber; + } else { + uint64_t frameNumber = 0; + if (!drflac__read_utf8_coded_number(bs, &frameNumber)) { + return DR_FALSE; + } + header->frameNumber = (uint32_t)frameNumber; // <-- Safe cast. + header->sampleNumber = 0; + } + + + if (blockSize == 1) { + header->blockSize = 192; + } else if (blockSize >= 2 && blockSize <= 5) { + header->blockSize = 576 * (1 << (blockSize - 2)); + } else if (blockSize == 6) { + if (!drflac__read_uint16(bs, 8, &header->blockSize)) { + return DR_FALSE; + } + header->blockSize += 1; + } else if (blockSize == 7) { + if (!drflac__read_uint16(bs, 16, &header->blockSize)) { + return DR_FALSE; + } + header->blockSize += 1; + } else { + header->blockSize = 256 * (1 << (blockSize - 8)); + } + + + if (sampleRate <= 11) { + header->sampleRate = sampleRateTable[sampleRate]; + } else if (sampleRate == 12) { + if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { + return DR_FALSE; + } + header->sampleRate *= 1000; + } else if (sampleRate == 13) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DR_FALSE; + } + } else if (sampleRate == 14) { + if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { + return DR_FALSE; + } + header->sampleRate *= 10; + } else { + return DR_FALSE; // Invalid. + } + + + header->channelAssignment = channelAssignment; + + header->bitsPerSample = bitsPerSampleTable[bitsPerSample]; + if (header->bitsPerSample == 0) { + header->bitsPerSample = streaminfoBitsPerSample; + } + + if (drflac__read_uint8(bs, 8, &header->crc8) != 1) { + return DR_FALSE; + } + + return DR_TRUE; +} + +static drBool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +{ + uint8_t header; + if (!drflac__read_uint8(bs, 8, &header)) { + return DR_FALSE; + } + + // First bit should always be 0. + if ((header & 0x80) != 0) { + return DR_FALSE; + } + + int type = (header & 0x7E) >> 1; + if (type == 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + } else if (type == 1) { + pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + } else { + if ((type & 0x20) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (type & 0x1F) + 1; + } else if ((type & 0x08) != 0) { + pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (type & 0x07); + if (pSubframe->lpcOrder > 4) { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->lpcOrder = 0; + } + } else { + pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + } + } + + if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { + return DR_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 DR_FALSE; + } + pSubframe->wastedBitsPerSample = (unsigned char)wastedBitsPerSample + 1; + } + + return DR_TRUE; +} + +static drBool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, int32_t* pDecodedSamplesOut) +{ + assert(bs != NULL); + assert(frame != NULL); + + drflac_subframe* pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DR_FALSE; + } + + // Side channels require an extra bit per sample. Took a while to figure that one out... + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } + + // Need to handle wasted bits per sample. + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = pDecodedSamplesOut; + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + drflac__decode_samples__constant(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + drflac__decode_samples__verbatim(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + drflac__decode_samples__fixed(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); + } break; + + case DRFLAC_SUBFRAME_LPC: + { + drflac__decode_samples__lpc(bs, frame->header.blockSize, pSubframe->bitsPerSample, pSubframe->lpcOrder, pSubframe->pDecodedSamples); + } break; + + default: return DR_FALSE; + } + + return DR_TRUE; +} + +static drBool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +{ + assert(bs != NULL); + assert(frame != NULL); + + drflac_subframe* pSubframe = frame->subframes + subframeIndex; + if (!drflac__read_subframe_header(bs, pSubframe)) { + return DR_FALSE; + } + + // Side channels require an extra bit per sample. Took a while to figure that one out... + pSubframe->bitsPerSample = frame->header.bitsPerSample; + if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + pSubframe->bitsPerSample += 1; + } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + pSubframe->bitsPerSample += 1; + } + + // Need to handle wasted bits per sample. + pSubframe->bitsPerSample -= pSubframe->wastedBitsPerSample; + pSubframe->pDecodedSamples = NULL; + //pSubframe->pDecodedSamples = pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * subframeIndex); + + switch (pSubframe->subframeType) + { + case DRFLAC_SUBFRAME_CONSTANT: + { + if (!drflac__seek_bits(bs, pSubframe->bitsPerSample)) { + return DR_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_VERBATIM: + { + unsigned int bitsToSeek = frame->header.blockSize * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DR_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_FIXED: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DR_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + return DR_FALSE; + } + } break; + + case DRFLAC_SUBFRAME_LPC: + { + unsigned int bitsToSeek = pSubframe->lpcOrder * pSubframe->bitsPerSample; + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DR_FALSE; + } + + unsigned char lpcPrecision; + if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { + return DR_FALSE; + } + if (lpcPrecision == 15) { + return DR_FALSE; // Invalid. + } + lpcPrecision += 1; + + + bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; // +5 for shift. + if (!drflac__seek_bits(bs, bitsToSeek)) { + return DR_FALSE; + } + + if (!drflac__read_and_seek_residual(bs, frame->header.blockSize, pSubframe->lpcOrder)) { + return DR_FALSE; + } + } break; + + default: return DR_FALSE; + } + + return DR_TRUE; +} + + +static DRFLAC_INLINE uint8_t drflac__get_channel_count_from_channel_assignment(int8_t channelAssignment) +{ + assert(channelAssignment <= 10); + + uint8_t lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + return lookup[channelAssignment]; +} + +static drBool32 drflac__decode_frame(drflac* pFlac) +{ + // This function should be called while the stream is sitting on the first byte after the frame header. + memset(pFlac->currentFrame.subframes, 0, sizeof(pFlac->currentFrame.subframes)); + + int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + for (int i = 0; i < channelCount; ++i) + { + if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFrame, i, pFlac->pDecodedSamples + (pFlac->currentFrame.header.blockSize * i))) { + return DR_FALSE; + } + } + + // At the end of the frame sits the padding and CRC. We don't use these so we can just seek past. + if (!drflac__seek_bits(&pFlac->bs, (DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7) + 16)) { + return DR_FALSE; + } + + + pFlac->currentFrame.samplesRemaining = pFlac->currentFrame.header.blockSize * channelCount; + + return DR_TRUE; +} + +static drBool32 drflac__seek_frame(drflac* pFlac) +{ + int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + for (int i = 0; i < channelCount; ++i) + { + if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFrame, i)) { + return DR_FALSE; + } + } + + // Padding and CRC. + return drflac__seek_bits(&pFlac->bs, (DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7) + 16); +} + +static drBool32 drflac__read_and_decode_next_frame(drflac* pFlac) +{ + assert(pFlac != NULL); + + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DR_FALSE; + } + + return drflac__decode_frame(pFlac); +} + + +static void drflac__get_current_frame_sample_range(drflac* pFlac, uint64_t* pFirstSampleInFrameOut, uint64_t* pLastSampleInFrameOut) +{ + assert(pFlac != NULL); + + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + + uint64_t firstSampleInFrame = pFlac->currentFrame.header.sampleNumber; + if (firstSampleInFrame == 0) { + firstSampleInFrame = pFlac->currentFrame.header.frameNumber * pFlac->maxBlockSize*channelCount; + } + + uint64_t lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channelCount); + if (lastSampleInFrame > 0) { + lastSampleInFrame -= 1; // Needs to be zero based. + } + + + if (pFirstSampleInFrameOut) { + *pFirstSampleInFrameOut = firstSampleInFrame; + } + if (pLastSampleInFrameOut) { + *pLastSampleInFrameOut = lastSampleInFrame; + } +} + +static drBool32 drflac__seek_to_first_frame(drflac* pFlac) +{ + assert(pFlac != NULL); + + drBool32 result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos); + + memset(&pFlac->currentFrame, 0, sizeof(pFlac->currentFrame)); + return result; +} + +static DRFLAC_INLINE drBool32 drflac__seek_to_next_frame(drflac* pFlac) +{ + // This function should only ever be called while the decoder is sitting on the first byte past the FRAME_HEADER section. + assert(pFlac != NULL); + return drflac__seek_frame(pFlac); +} + +static drBool32 drflac__seek_to_frame_containing_sample(drflac* pFlac, uint64_t sampleIndex) +{ + assert(pFlac != NULL); + + if (!drflac__seek_to_first_frame(pFlac)) { + return DR_FALSE; + } + + uint64_t firstSampleInFrame = 0; + uint64_t lastSampleInFrame = 0; + for (;;) + { + // We need to read the frame's header in order to determine the range of samples it contains. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DR_FALSE; + } + + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + if (sampleIndex >= firstSampleInFrame && sampleIndex <= lastSampleInFrame) { + break; // The sample is in this frame. + } + + if (!drflac__seek_to_next_frame(pFlac)) { + return DR_FALSE; + } + } + + // If we get here we should be right at the start of the frame containing the sample. + return DR_TRUE; +} + +static drBool32 drflac__seek_to_sample__brute_force(drflac* pFlac, uint64_t sampleIndex) +{ + if (!drflac__seek_to_frame_containing_sample(pFlac, sampleIndex)) { + return DR_FALSE; + } + + // At this point we should be sitting on the first byte of the frame containing the sample. We need to decode every sample up to (but + // not including) the sample we're seeking to. + uint64_t firstSampleInFrame = 0; + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, NULL); + + assert(firstSampleInFrame <= sampleIndex); + size_t samplesToDecode = (size_t)(sampleIndex - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535. + if (samplesToDecode == 0) { + return DR_TRUE; + } + + // At this point we are just sitting on the byte after the frame header. We need to decode the frame before reading anything from it. + if (!drflac__decode_frame(pFlac)) { + return DR_FALSE; + } + + return drflac_read_s32(pFlac, samplesToDecode, NULL) != 0; +} + + +static drBool32 drflac__seek_to_sample__seek_table(drflac* pFlac, uint64_t sampleIndex) +{ + assert(pFlac != NULL); + + if (pFlac->seektablePos == 0) { + return DR_FALSE; + } + + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->seektablePos)) { + return DR_FALSE; + } + + // The number of seek points is derived from the size of the SEEKTABLE block. + uint32_t seekpointCount = pFlac->seektableSize / 18; // 18 = the size of each seek point. + if (seekpointCount == 0) { + return DR_FALSE; // Would this ever happen? + } + + + drflac_seekpoint closestSeekpoint = {0, 0, 0}; + + uint32_t seekpointsRemaining = seekpointCount; + while (seekpointsRemaining > 0) + { + drflac_seekpoint seekpoint; + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.firstSample)) { + break; + } + if (!drflac__read_uint64(&pFlac->bs, 64, &seekpoint.frameOffset)) { + break; + } + if (!drflac__read_uint16(&pFlac->bs, 16, &seekpoint.sampleCount)) { + break; + } + + if (seekpoint.firstSample * pFlac->channels > sampleIndex) { + break; + } + + closestSeekpoint = seekpoint; + seekpointsRemaining -= 1; + } + + // At this point we should have found the seekpoint closest to our sample. We need to seek to it using basically the same + // technique as we use with the brute force method. + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos + closestSeekpoint.frameOffset)) { + return DR_FALSE; + } + + + uint64_t firstSampleInFrame = 0; + uint64_t lastSampleInFrame = 0; + for (;;) + { + // We need to read the frame's header in order to determine the range of samples it contains. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DR_FALSE; + } + + drflac__get_current_frame_sample_range(pFlac, &firstSampleInFrame, &lastSampleInFrame); + if (sampleIndex >= firstSampleInFrame && sampleIndex <= lastSampleInFrame) { + break; // The sample is in this frame. + } + + if (!drflac__seek_to_next_frame(pFlac)) { + return DR_FALSE; + } + } + + assert(firstSampleInFrame <= sampleIndex); + + // At this point we are just sitting on the byte after the frame header. We need to decode the frame before reading anything from it. + if (!drflac__decode_frame(pFlac)) { + return DR_FALSE; + } + + size_t samplesToDecode = (size_t)(sampleIndex - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; +} + + +#ifndef DR_FLAC_NO_OGG +typedef struct +{ + uint8_t capturePattern[4]; // Should be "OggS" + uint8_t structureVersion; // Always 0. + uint8_t headerType; + uint64_t granulePosition; + uint32_t serialNumber; + uint32_t sequenceNumber; + uint32_t checksum; + uint8_t segmentCount; + uint8_t segmentTable[255]; +} drflac_ogg_page_header; +#endif + +typedef struct +{ + drflac_read_proc onRead; + drflac_seek_proc onSeek; + drflac_meta_proc onMeta; + void* pUserData; + void* pUserDataMD; + drflac_container container; + uint32_t sampleRate; + uint8_t channels; + uint8_t bitsPerSample; + uint64_t totalSampleCount; + uint16_t maxBlockSize; + uint64_t runningFilePos; + drBool32 hasMetadataBlocks; + +#ifndef DR_FLAC_NO_OGG + uint32_t oggSerial; + uint64_t oggFirstBytePos; + drflac_ogg_page_header oggBosHeader; +#endif +} drflac_init_info; + +static DRFLAC_INLINE void drflac__decode_block_header(uint32_t blockHeader, uint8_t* isLastBlock, uint8_t* blockType, uint32_t* blockSize) +{ + blockHeader = drflac__be2host_32(blockHeader); + *isLastBlock = (blockHeader & (0x01 << 31)) >> 31; + *blockType = (blockHeader & (0x7F << 24)) >> 24; + *blockSize = (blockHeader & 0xFFFFFF); +} + +static DRFLAC_INLINE drBool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, uint8_t* isLastBlock, uint8_t* blockType, uint32_t* blockSize) +{ + uint32_t blockHeader; + if (onRead(pUserData, &blockHeader, 4) != 4) { + return DR_FALSE; + } + + drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return DR_TRUE; +} + +drBool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +{ + // min/max block size. + uint32_t blockSizes; + if (onRead(pUserData, &blockSizes, 4) != 4) { + return DR_FALSE; + } + + // min/max frame size. + uint64_t frameSizes = 0; + if (onRead(pUserData, &frameSizes, 6) != 6) { + return DR_FALSE; + } + + // Sample rate, channels, bits per sample and total sample count. + uint64_t importantProps; + if (onRead(pUserData, &importantProps, 8) != 8) { + return DR_FALSE; + } + + // MD5 + uint8_t md5[16]; + if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { + return DR_FALSE; + } + + blockSizes = drflac__be2host_32(blockSizes); + frameSizes = drflac__be2host_64(frameSizes); + importantProps = drflac__be2host_64(importantProps); + + pStreamInfo->minBlockSize = (blockSizes & 0xFFFF0000) >> 16; + pStreamInfo->maxBlockSize = blockSizes & 0x0000FFFF; + pStreamInfo->minFrameSize = (uint32_t)((frameSizes & 0xFFFFFF0000000000ULL) >> 40ULL); + pStreamInfo->maxFrameSize = (uint32_t)((frameSizes & 0x000000FFFFFF0000ULL) >> 16ULL); + pStreamInfo->sampleRate = (uint32_t)((importantProps & 0xFFFFF00000000000ULL) >> 44ULL); + pStreamInfo->channels = (uint8_t )((importantProps & 0x00000E0000000000ULL) >> 41ULL) + 1; + pStreamInfo->bitsPerSample = (uint8_t )((importantProps & 0x000001F000000000ULL) >> 36ULL) + 1; + pStreamInfo->totalSampleCount = (importantProps & 0x0000000FFFFFFFFFULL) * pStreamInfo->channels; + memcpy(pStreamInfo->md5, md5, sizeof(md5)); + + return DR_TRUE; +} + +drBool32 drflac__read_and_decode_metadata(drflac* pFlac) +{ + assert(pFlac != NULL); + + // 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. + uint64_t runningFilePos = 42; + uint64_t seektablePos = 0; + uint32_t seektableSize = 0; + + for (;;) + { + uint8_t isLastBlock = 0; + uint8_t blockType; + uint32_t blockSize; + if (!drflac__read_and_decode_block_header(pFlac->bs.onRead, pFlac->bs.pUserData, &isLastBlock, &blockType, &blockSize)) { + return DR_FALSE; + } + runningFilePos += 4; + + + drflac_metadata metadata; + metadata.type = blockType; + metadata.pRawData = NULL; + metadata.rawDataSize = 0; + + switch (blockType) + { + case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + { + if (pFlac->onMeta) { + void* pRawData = malloc(blockSize); + if (pRawData == NULL) { + return DR_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + free(pRawData); + return DR_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + metadata.data.application.id = drflac__be2host_32(*(uint32_t*)pRawData); + metadata.data.application.pData = (const void*)((uint8_t*)pRawData + sizeof(uint32_t)); + metadata.data.application.dataSize = blockSize - sizeof(uint32_t); + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + free(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + { + seektablePos = runningFilePos; + seektableSize = blockSize; + + if (pFlac->onMeta) { + void* pRawData = malloc(blockSize); + if (pRawData == NULL) { + return DR_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + free(pRawData); + return DR_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 (uint32_t iSeekpoint = 0; iSeekpoint < metadata.data.seektable.seekpointCount; ++iSeekpoint) { + drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; + pSeekpoint->firstSample = drflac__be2host_64(pSeekpoint->firstSample); + pSeekpoint->frameOffset = drflac__be2host_64(pSeekpoint->frameOffset); + pSeekpoint->sampleCount = drflac__be2host_16(pSeekpoint->sampleCount); + } + + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + free(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + { + if (pFlac->onMeta) { + void* pRawData = malloc(blockSize); + if (pRawData == NULL) { + return DR_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + free(pRawData); + return DR_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + metadata.data.vorbis_comment.vendorLength = drflac__le2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; + metadata.data.vorbis_comment.commentCount = drflac__le2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.vorbis_comment.comments = pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + free(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + { + if (pFlac->onMeta) { + void* pRawData = malloc(blockSize); + if (pRawData == NULL) { + return DR_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + free(pRawData); + return DR_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + memcpy(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(uint64_t*)pRunningData); pRunningData += 4; + metadata.data.cuesheet.isCD = ((pRunningData[0] & 0x80) >> 7) != 0; pRunningData += 259; + metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; + metadata.data.cuesheet.pTrackData = (const uint8_t*)pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + free(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + { + if (pFlac->onMeta) { + void* pRawData = malloc(blockSize); + if (pRawData == NULL) { + return DR_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + free(pRawData); + return DR_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + + const char* pRunningData = (const char*)pRawData; + metadata.data.picture.type = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; + metadata.data.picture.descriptionLength = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.description = pRunningData; + metadata.data.picture.width = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.height = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = drflac__be2host_32(*(uint32_t*)pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const uint8_t*)pRunningData; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + free(pRawData); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + { + if (pFlac->onMeta) { + metadata.data.padding.unused = 0; + + // Padding doesn't have anything meaningful in it, so just skip over it. + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + return DR_FALSE; + } + + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + } + } break; + + case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + { + // Invalid chunk. Just skip over this one. + if (pFlac->onMeta) { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + return DR_FALSE; + } + } + } + + 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 (pFlac->onMeta) { + void* pRawData = malloc(blockSize); + if (pRawData == NULL) { + return DR_FALSE; + } + + if (pFlac->bs.onRead(pFlac->bs.pUserData, pRawData, blockSize) != blockSize) { + free(pRawData); + return DR_FALSE; + } + + metadata.pRawData = pRawData; + metadata.rawDataSize = blockSize; + pFlac->onMeta(pFlac->pUserDataMD, &metadata); + + free(pRawData); + } + } break; + } + + // If we're not handling metadata, just skip over the block. If we are, it will have been handled earlier in the switch statement above. + if (pFlac->onMeta == NULL) { + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, blockSize, drflac_seek_origin_current)) { + return DR_FALSE; + } + } + + runningFilePos += blockSize; + if (isLastBlock) { + break; + } + } + + pFlac->seektablePos = seektablePos; + pFlac->seektableSize = seektableSize; + pFlac->firstFramePos = runningFilePos; + + return DR_TRUE; +} + +drBool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +{ + (void)onSeek; + + // Pre: The bit stream should be sitting just past the 4-byte id header. + + pInit->container = drflac_container_native; + + // The first metadata block should be the STREAMINFO block. + uint8_t isLastBlock; + uint8_t blockType; + uint32_t blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DR_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return DR_FALSE; // Invalid block type. First block must be the STREAMINFO block. + } + + + drflac_streaminfo streaminfo; + if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return DR_FALSE; + } + + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; // Don't care about the min block size - only the max (used for determining the size of the memory allocation). + + 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->hasMetadataBlocks = !isLastBlock; + return DR_TRUE; +} + +#ifndef DR_FLAC_NO_OGG +static DRFLAC_INLINE drBool32 drflac_ogg__is_capture_pattern(uint8_t pattern[4]) +{ + return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; +} + +static DRFLAC_INLINE uint32_t drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +{ + return 27 + pHeader->segmentCount; +} + +static DRFLAC_INLINE uint32_t drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +{ + uint32_t pageBodySize = 0; + for (int i = 0; i < pHeader->segmentCount; ++i) { + pageBodySize += pHeader->segmentTable[i]; + } + + return pageBodySize; +} + +drBool32 drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, uint32_t* pHeaderSize) +{ + if (onRead(pUserData, &pHeader->structureVersion, 1) != 1 || pHeader->structureVersion != 0) { + return DR_FALSE; // Unknown structure version. Possibly corrupt stream. + } + if (onRead(pUserData, &pHeader->headerType, 1) != 1) { + return DR_FALSE; + } + if (onRead(pUserData, &pHeader->granulePosition, 8) != 8) { + return DR_FALSE; + } + if (onRead(pUserData, &pHeader->serialNumber, 4) != 4) { + return DR_FALSE; + } + if (onRead(pUserData, &pHeader->sequenceNumber, 4) != 4) { + return DR_FALSE; + } + if (onRead(pUserData, &pHeader->checksum, 4) != 4) { + return DR_FALSE; + } + if (onRead(pUserData, &pHeader->segmentCount, 1) != 1 || pHeader->segmentCount == 0) { + return DR_FALSE; // Should not have a segment count of 0. + } + if (onRead(pUserData, &pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { + return DR_FALSE; + } + + if (pHeaderSize) *pHeaderSize = (27 + pHeader->segmentCount); + return DR_TRUE; +} + +drBool32 drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, uint32_t* pHeaderSize) +{ + uint8_t id[4]; + if (onRead(pUserData, id, 4) != 4) { + return DR_FALSE; + } + + if (id[0] != 'O' || id[1] != 'g' || id[2] != 'g' || id[3] != 'S') { + return DR_FALSE; + } + + return drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pHeaderSize); +} + + +// 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 architecured +// 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. + uint64_t currentBytePos; // The position of the byte we are sitting on in the physical byte stream. Used for efficient seeking. + uint64_t firstBytePos; // The position of the first byte in the physical bitstream. Points to the start of the "OggS" identifier of the FLAC bos page. + uint32_t 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; + uint32_t bytesRemainingInPage; +} 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 drBool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, uint64_t 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 DR_FALSE; + } + oggbs->currentBytePos = offset; + + return DR_TRUE; + } else { + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { + return DR_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 DR_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 DR_FALSE; + } + oggbs->currentBytePos += offset; + + return DR_TRUE; + } +} + +static drBool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs) +{ + drflac_ogg_page_header header; + for (;;) + { + uint32_t headerSize; + if (!drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &headerSize)) { + return DR_FALSE; + } + oggbs->currentBytePos += headerSize; + + + uint32_t pageBodySize = drflac_ogg__get_page_body_size(&header); + + if (header.serialNumber == oggbs->serialNumber) { + oggbs->currentPageHeader = header; + oggbs->bytesRemainingInPage = pageBodySize; + return DR_TRUE; + } + + // If we get here it means the page is not a FLAC page - skip it. + if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { // <-- Safe cast - maximum size of a page is way below that of an int. + return DR_FALSE; + } + } +} + +// Function below is unused at the moment, but I might be re-adding it later. +#if 0 +static uint8_t drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, uint8_t* pBytesRemainingInSeg) +{ + uint32_t bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + uint8_t iSeg = 0; + uint32_t iByte = 0; + while (iByte < bytesConsumedInPage) + { + uint8_t segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (iByte + segmentSize > bytesConsumedInPage) { + break; + } else { + iSeg += 1; + iByte += segmentSize; + } + } + + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (uint8_t)(bytesConsumedInPage - iByte); + return iSeg; +} + +static drBool32 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 (;;) // <-- Loop over pages. + { + drBool32 atEndOfPage = DR_FALSE; + + uint8_t bytesRemainingInSeg; + uint8_t iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + + uint32_t bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (uint8_t iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + uint8_t segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + if (segmentSize < 255) { + if (iSeg == oggbs->currentPageHeader.segmentCount-1) { + atEndOfPage = DR_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 frame. + 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 DR_FALSE; + } + + // If it's a fresh packet it most likely means we're at the next packet. + if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { + return DR_TRUE; + } + } + else + { + // We're at the next frame. + return DR_TRUE; + } + } +} + +static drBool32 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; + assert(oggbs != NULL); + + uint8_t* pRunningBufferOut = (uint8_t*)bufferOut; + + // Reading is done page-by-page. If we've run out of bytes in the page we need to move to the next one. + size_t bytesRead = 0; + while (bytesRead < bytesToRead) + { + size_t bytesRemainingToRead = bytesToRead - bytesRead; + + if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { + bytesRead += oggbs->onRead(oggbs->pUserData, pRunningBufferOut, bytesRemainingToRead); + oggbs->bytesRemainingInPage -= (uint32_t)bytesRemainingToRead; + break; + } + + // If we get here it means some of the requested data is contained in the next pages. + if (oggbs->bytesRemainingInPage > 0) { + size_t bytesJustRead = oggbs->onRead(oggbs->pUserData, pRunningBufferOut, oggbs->bytesRemainingInPage); + bytesRead += bytesJustRead; + pRunningBufferOut += bytesJustRead; + + if (bytesJustRead != oggbs->bytesRemainingInPage) { + break; // Ran out of data. + } + } + + assert(bytesRemainingToRead > 0); + if (!drflac_oggbs__goto_next_page(oggbs)) { + break; // Failed to go to the next chunk. Might have simply hit the end of the stream. + } + } + + oggbs->currentBytePos += bytesRead; + return bytesRead; +} + +static drBool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + assert(oggbs != NULL); + assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + // Seeking is always forward which makes things a lot simpler. + if (origin == drflac_seek_origin_start) { + int startBytePos = (int)oggbs->firstBytePos + (79-42); // 79 = size of bos page; 42 = size of FLAC header data. Seek up to the first byte of the native FLAC data. + if (!drflac_oggbs__seek_physical(oggbs, startBytePos, drflac_seek_origin_start)) { + return DR_FALSE; + } + + oggbs->currentPageHeader = oggbs->bosPageHeader; + oggbs->bytesRemainingInPage = 42; // 42 = size of the native FLAC header data. That's our start point for seeking. + + return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + } + + + assert(origin == drflac_seek_origin_current); + + int bytesSeeked = 0; + while (bytesSeeked < offset) + { + int bytesRemainingToSeek = offset - bytesSeeked; + assert(bytesRemainingToSeek >= 0); + + if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { + if (!drflac_oggbs__seek_physical(oggbs, bytesRemainingToSeek, drflac_seek_origin_current)) { + return DR_FALSE; + } + + bytesSeeked += bytesRemainingToSeek; + oggbs->bytesRemainingInPage -= bytesRemainingToSeek; + break; + } + + // If we get here it means some of the requested data is contained in the next pages. + if (oggbs->bytesRemainingInPage > 0) { + if (!drflac_oggbs__seek_physical(oggbs, oggbs->bytesRemainingInPage, drflac_seek_origin_current)) { + return DR_FALSE; + } + + bytesSeeked += (int)oggbs->bytesRemainingInPage; + } + + assert(bytesRemainingToSeek > 0); + if (!drflac_oggbs__goto_next_page(oggbs)) { + break; // Failed to go to the next chunk. Might have simply hit the end of the stream. + } + } + + return DR_TRUE; +} + +drBool32 drflac_ogg__seek_to_sample(drflac* pFlac, uint64_t sample) +{ + drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels); + + uint64_t originalBytePos = oggbs->currentBytePos; // For recovery. + + // First seek to the first frame. + if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFramePos)) { + return DR_FALSE; + } + oggbs->bytesRemainingInPage = 0; + + uint64_t runningGranulePosition = 0; + uint64_t runningFrameBytePos = oggbs->currentBytePos; // <-- Points to the OggS identifier. + for (;;) + { + if (!drflac_oggbs__goto_next_page(oggbs)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DR_FALSE; // Never did find that sample... + } + + runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader); + if (oggbs->currentPageHeader.granulePosition*pFlac->channels >= sample) { + 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) { + uint8_t firstBytesInPage[2]; + if (drflac_oggbs__read_physical(oggbs, firstBytesInPage, 2) != 2) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DR_FALSE; + } + if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { // <-- Does the page begin with a frame's sync code? + runningGranulePosition = oggbs->currentPageHeader.granulePosition*pFlac->channels; + } + + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->bytesRemainingInPage-2, drflac_seek_origin_current)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DR_FALSE; + } + + continue; + } + } + + if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->bytesRemainingInPage, drflac_seek_origin_current)) { + drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); + return DR_FALSE; + } + } + + + // 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 DR_FALSE; + } + if (!drflac_oggbs__goto_next_page(oggbs)) { + return DR_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. + uint64_t firstSampleInFrame = runningGranulePosition; + for (;;) + { + // NOTE for later: When using Ogg's page/segment based seeking later on we can't use this function (or any drflac__* + // reading functions) because otherwise it will pull extra data for use in it's own internal caches which will then + // break the positioning of the read pointer for the Ogg bitstream. + if (!drflac__read_next_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFrame.header)) { + return DR_FALSE; + } + + int channels = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + uint64_t lastSampleInFrame = firstSampleInFrame + (pFlac->currentFrame.header.blockSize*channels); + lastSampleInFrame -= 1; // <-- Zero based. + + if (sample >= firstSampleInFrame && sample <= lastSampleInFrame) { + break; // The sample is in this frame. + } + + + // If we get here it means the sample is not in this frame so we need to move to the next one. Now the cool thing + // with Ogg is that we can efficiently seek past the frame by looking at the lacing values of each segment in + // the page. + firstSampleInFrame = lastSampleInFrame+1; + +#if 1 + // Slow way. This uses the native FLAC decoder to seek past the frame. This is slow because it needs to do a partial + // decode of the frame. Although this is how the native version works, we can use Ogg's framing system to make it + // more efficient. Leaving this here for reference and to use as a basis for debugging purposes. + if (!drflac__seek_to_next_frame(pFlac)) { + return DR_FALSE; + } +#else + // TODO: This is not yet complete. See note at the top of this loop body. + + // Fast(er) way. This uses Ogg's framing system to seek past the frame. This should be much more efficient than the + // native FLAC seeking. + if (!drflac_oggbs__seek_to_next_frame(oggbs)) { + return DR_FALSE; + } +#endif + } + + assert(firstSampleInFrame <= sample); + + if (!drflac__decode_frame(pFlac)) { + return DR_FALSE; + } + + size_t samplesToDecode = (size_t)(sample - firstSampleInFrame); // <-- Safe cast because the maximum number of samples in a frame is 65535. + return drflac_read_s32(pFlac, samplesToDecode, NULL) == samplesToDecode; +} + + +drBool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +{ + // Pre: The bit stream should be sitting just past the 4-byte OggS capture pattern. + + pInit->container = drflac_container_ogg; + pInit->oggFirstBytePos = 0; + + // We'll get here if the first 4 bytes of the stream were the OggS capture pattern, however it doesn't necessarily mean the + // stream includes FLAC encoded audio. To check for this we need to scan the beginning-of-stream page markers and check if + // any match the FLAC specification. Important to keep in mind that the stream may be multiplexed. + drflac_ogg_page_header header; + + uint32_t headerSize; + if (!drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &headerSize)) { + return DR_FALSE; + } + pInit->runningFilePos = headerSize; + + for (;;) + { + // Break if we're past the beginning of stream page. + if ((header.headerType & 0x02) == 0) { + return DR_FALSE; + } + + + // Check if it's a FLAC header. + int pageBodySize = drflac_ogg__get_page_body_size(&header); + if (pageBodySize == 51) // 51 = the lacing value of the FLAC header packet. + { + // It could be a FLAC page... + uint32_t bytesRemainingInPage = pageBodySize; + + uint8_t packetType; + if (onRead(pUserData, &packetType, 1) != 1) { + return DR_FALSE; + } + + bytesRemainingInPage -= 1; + if (packetType == 0x7F) + { + // Increasingly more likely to be a FLAC page... + uint8_t sig[4]; + if (onRead(pUserData, sig, 4) != 4) { + return DR_FALSE; + } + + bytesRemainingInPage -= 4; + if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') + { + // Almost certainly a FLAC page... + uint8_t mappingVersion[2]; + if (onRead(pUserData, mappingVersion, 2) != 2) { + return DR_FALSE; + } + + if (mappingVersion[0] != 1) { + return DR_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 DR_FALSE; + } + + // Expecting the native FLAC signature "fLaC". + if (onRead(pUserData, sig, 4) != 4) { + return DR_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. + uint8_t isLastBlock; + uint8_t blockType; + uint32_t blockSize; + if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return DR_FALSE; + } + + if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return DR_FALSE; // Invalid block type. First block must be the STREAMINFO block. + } + + drflac_streaminfo streaminfo; + if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) + { + // Success! + pInit->sampleRate = streaminfo.sampleRate; + pInit->channels = streaminfo.channels; + pInit->bitsPerSample = streaminfo.bitsPerSample; + pInit->totalSampleCount = streaminfo.totalSampleCount; + pInit->maxBlockSize = streaminfo.maxBlockSize; + + 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 DR_FALSE; + } + } + else + { + // Invalid file. + return DR_FALSE; + } + } + else + { + // Not a FLAC header. Skip it. + if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { + return DR_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 DR_FALSE; + } + } + } + else + { + if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { + return DR_FALSE; + } + } + + pInit->runningFilePos += pageBodySize; + + + // Read the header of the next page. + if (!drflac_ogg__read_page_header(onRead, pUserData, &header, &headerSize)) { + return DR_FALSE; + } + pInit->runningFilePos += headerSize; + } + + + // 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 initialiation phase for Ogg is to create the + // Ogg bistream object. + pInit->hasMetadataBlocks = DR_TRUE; // <-- Always have at least VORBIS_COMMENT metadata block. + return DR_TRUE; +} +#endif + +drBool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +{ + if (pInit == NULL || onRead == NULL || onSeek == NULL) { + return DR_FALSE; + } + + pInit->onRead = onRead; + pInit->onSeek = onSeek; + pInit->onMeta = onMeta; + pInit->pUserData = pUserData; + pInit->pUserDataMD = pUserDataMD; + + uint8_t id[4]; + if (onRead(pUserData, id, 4) != 4) { + return DR_FALSE; + } + + if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { + return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD); + } + +#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); + } +#endif + + // Unsupported container. + return DR_FALSE; +} + +void drflac__init_from_info(drflac* pFlac, drflac_init_info* pInit) +{ + assert(pFlac != NULL); + assert(pInit != NULL); + + memset(pFlac, 0, sizeof(*pFlac)); + pFlac->bs.onRead = pInit->onRead; + pFlac->bs.onSeek = pInit->onSeek; + pFlac->bs.pUserData = pInit->pUserData; + pFlac->bs.nextL2Line = sizeof(pFlac->bs.cacheL2) / sizeof(pFlac->bs.cacheL2[0]); // <-- Initialize to this to force a client-side data retrieval right from the start. + pFlac->bs.consumedBits = sizeof(pFlac->bs.cache)*8; + + pFlac->onMeta = pInit->onMeta; + pFlac->pUserDataMD = pInit->pUserDataMD; + pFlac->maxBlockSize = pInit->maxBlockSize; + pFlac->sampleRate = pInit->sampleRate; + pFlac->channels = (uint8_t)pInit->channels; + pFlac->bitsPerSample = (uint8_t)pInit->bitsPerSample; + pFlac->totalSampleCount = pInit->totalSampleCount; + pFlac->container = pInit->container; +} + +drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD) +{ + drflac_init_info init; + if (!drflac__init_private(&init, onRead, onSeek, onMeta, pUserData, pUserDataMD)) { + return NULL; + } + + size_t allocationSize = sizeof(drflac); + allocationSize += init.maxBlockSize * init.channels * sizeof(int32_t); + //allocationSize += init.seektableSize; + + +#ifndef DR_FLAC_NO_OGG + // There's additional data required for Ogg streams. + if (init.container == drflac_container_ogg) { + allocationSize += sizeof(drflac_oggbs); + } +#endif + + drflac* pFlac = (drflac*)malloc(allocationSize); + drflac__init_from_info(pFlac, &init); + pFlac->pDecodedSamples = (int32_t*)pFlac->pExtraData; + +#ifndef DR_FLAC_NO_OGG + if (init.container == drflac_container_ogg) { + drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + init.maxBlockSize*init.channels); + 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; + + // 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*)oggbs; + } +#endif + + // Decode metadata before returning. + if (init.hasMetadataBlocks) { + if (!drflac__read_and_decode_metadata(pFlac)) { + free(pFlac); + return NULL; + } + } + + return pFlac; +} + + + +#ifndef DR_FLAC_NO_STDIO +typedef void* drflac_file; + +#if defined(DR_FLAC_NO_WIN32_IO) || !defined(_WIN32) +#include + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); +} + +static drBool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; +} + +static drflac_file drflac__open_file_handle(const char* filename) +{ + FILE* pFile; +#ifdef _MSC_VER + if (fopen_s(&pFile, filename, "rb") != 0) { + return NULL; + } +#else + pFile = fopen(filename, "rb"); + if (pFile == NULL) { + return NULL; + } +#endif + + return (drflac_file)pFile; +} + +static void drflac__close_file_handle(drflac_file file) +{ + fclose((FILE*)file); +} +#else +#include + +static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + assert(bytesToRead < 0xFFFFFFFF); // dr_flac will never request huge amounts of data at a time. This is a safe assertion. + + DWORD bytesRead; + ReadFile((HANDLE)pUserData, bufferOut, (DWORD)bytesToRead, &bytesRead, NULL); + + return (size_t)bytesRead; +} + +static drBool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +{ + assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + return SetFilePointer((HANDLE)pUserData, offset, NULL, (origin == drflac_seek_origin_current) ? FILE_CURRENT : FILE_BEGIN) != INVALID_SET_FILE_POINTER; +} + +static drflac_file drflac__open_file_handle(const char* filename) +{ + HANDLE hFile = CreateFileA(filename, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + return NULL; + } + + return (drflac_file)hFile; +} + +static void drflac__close_file_handle(drflac_file file) +{ + CloseHandle((HANDLE)file); +} +#endif + + +drflac* drflac_open_file(const char* filename) +{ + drflac_file file = drflac__open_file_handle(filename); + if (file == NULL) { + return NULL; + } + + drflac* pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)file); + if (pFlac == NULL) { + drflac__close_file_handle(file); + return NULL; + } + + return pFlac; +} + +drflac* drflac_open_file_with_metadata(const char* filename, drflac_meta_proc onMeta, void* pUserData) +{ + drflac_file file = drflac__open_file_handle(filename); + if (file == NULL) { + return NULL; + } + + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, (void*)file, pUserData); + if (pFlac == NULL) { + drflac__close_file_handle(file); + return pFlac; + } + + return pFlac; +} +#endif //DR_FLAC_NO_STDIO + +static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + assert(memoryStream != NULL); + assert(memoryStream->dataSize >= memoryStream->currentReadPos); + + size_t bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; + if (bytesToRead > bytesRemaining) { + bytesToRead = bytesRemaining; + } + + if (bytesToRead > 0) { + memcpy(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + memoryStream->currentReadPos += bytesToRead; + } + + return bytesToRead; +} + +static drBool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +{ + drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + assert(memoryStream != NULL); + assert(offset > 0 || (offset == 0 && origin == drflac_seek_origin_start)); + + if (origin == drflac_seek_origin_current) { + if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { + memoryStream->currentReadPos += offset; + } else { + memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. + } + } else { + if ((uint32_t)offset <= memoryStream->dataSize) { + memoryStream->currentReadPos = offset; + } else { + memoryStream->currentReadPos = memoryStream->dataSize; // Trying to seek too far forward. + } + } + + return DR_TRUE; +} + +drflac* drflac_open_memory(const void* data, size_t dataSize) +{ + drflac__memory_stream memoryStream; + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + drflac* pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + // This is an awful hack... +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels); + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + +drflac* drflac_open_memory_with_metadata(const void* data, size_t dataSize, drflac_meta_proc onMeta, void* pUserData) +{ + drflac__memory_stream memoryStream; + memoryStream.data = (const unsigned char*)data; + memoryStream.dataSize = dataSize; + memoryStream.currentReadPos = 0; + drflac* pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, &memoryStream, pUserData); + if (pFlac == NULL) { + return NULL; + } + + pFlac->memoryStream = memoryStream; + + // This is an awful hack... +#ifndef DR_FLAC_NO_OGG + if (pFlac->container == drflac_container_ogg) + { + drflac_oggbs* oggbs = (drflac_oggbs*)(((int32_t*)pFlac->pExtraData) + pFlac->maxBlockSize*pFlac->channels); + oggbs->pUserData = &pFlac->memoryStream; + } + else +#endif + { + pFlac->bs.pUserData = &pFlac->memoryStream; + } + + return pFlac; +} + + + +drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, NULL, pUserData, pUserData); +} + +drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData) +{ + return drflac_open_with_metadata_private(onRead, onSeek, onMeta, pUserData, pUserData); +} + +void drflac_close(drflac* pFlac) +{ + if (pFlac == NULL) { + return; + } + +#ifndef DR_FLAC_NO_STDIO + // If we opened the file with drflac_open_file() we will want to close the file handle. We can know whether or not drflac_open_file() + // was used by looking at the callbacks. + if (pFlac->bs.onRead == drflac__on_read_stdio) { + drflac__close_file_handle((drflac_file)pFlac->bs.pUserData); + } + +#ifndef DR_FLAC_NO_OGG + // Need to clean up Ogg streams a bit differently due to the way the bit streaming is chained. + if (pFlac->container == drflac_container_ogg) { + assert(pFlac->bs.onRead == drflac__on_read_ogg); + drflac_oggbs* oggbs = (drflac_oggbs*)((int32_t*)pFlac->pExtraData + pFlac->maxBlockSize*pFlac->channels); + if (oggbs->onRead == drflac__on_read_stdio) { + drflac__close_file_handle((drflac_file)oggbs->pUserData); + } + } +#endif +#endif + + free(pFlac); +} + +uint64_t drflac__read_s32__misaligned(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferOut) +{ + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + + // We should never be calling this when the number of samples to read is >= the sample count. + assert(samplesToRead < channelCount); + assert(pFlac->currentFrame.samplesRemaining > 0 && samplesToRead <= pFlac->currentFrame.samplesRemaining); + + + uint64_t samplesRead = 0; + while (samplesToRead > 0) + { + uint64_t totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + uint64_t samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + unsigned int channelIndex = samplesReadFromFrameSoFar % channelCount; + + uint64_t nextSampleInFrame = samplesReadFromFrameSoFar / channelCount; + + int decodedSample = 0; + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + if (channelIndex == 0) { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } else { + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int left = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = left - side; + } + + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + if (channelIndex == 0) { + int side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + int right = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + decodedSample = side + right; + } else { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } + + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + int mid; + int side; + if (channelIndex == 0) { + mid = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 1].pDecodedSamples[nextSampleInFrame]; + + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid + side) >> 1; + } else { + mid = pFlac->currentFrame.subframes[channelIndex - 1].pDecodedSamples[nextSampleInFrame]; + side = pFlac->currentFrame.subframes[channelIndex + 0].pDecodedSamples[nextSampleInFrame]; + + mid = (((unsigned int)mid) << 1) | (side & 0x01); + decodedSample = (mid - side) >> 1; + } + + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + decodedSample = pFlac->currentFrame.subframes[channelIndex].pDecodedSamples[nextSampleInFrame]; + } break; + } + + + decodedSample <<= ((32 - pFlac->bitsPerSample) + pFlac->currentFrame.subframes[channelIndex].wastedBitsPerSample); + + if (bufferOut) { + *bufferOut++ = decodedSample; + } + + samplesRead += 1; + pFlac->currentFrame.samplesRemaining -= 1; + samplesToRead -= 1; + } + + return samplesRead; +} + +uint64_t drflac__seek_forward_by_samples(drflac* pFlac, uint64_t samplesToRead) +{ + uint64_t samplesRead = 0; + while (samplesToRead > 0) + { + if (pFlac->currentFrame.samplesRemaining == 0) + { + if (!drflac__read_and_decode_next_frame(pFlac)) { + break; // Couldn't read the next frame, so just break from the loop and return. + } + } + else + { + samplesRead += 1; + pFlac->currentFrame.samplesRemaining -= 1; + samplesToRead -= 1; + } + } + + return samplesRead; +} + +uint64_t drflac_read_s32(drflac* pFlac, uint64_t samplesToRead, int32_t* bufferOut) +{ + // Note that is allowed to be null, in which case this will be treated as something like a seek. + if (pFlac == NULL || samplesToRead == 0) { + return 0; + } + + if (bufferOut == NULL) { + return drflac__seek_forward_by_samples(pFlac, samplesToRead); + } + + + uint64_t samplesRead = 0; + while (samplesToRead > 0) + { + // If we've run out of samples in this frame, go to the next. + if (pFlac->currentFrame.samplesRemaining == 0) + { + if (!drflac__read_and_decode_next_frame(pFlac)) { + break; // Couldn't read the next frame, so just break from the loop and return. + } + } + else + { + // Here is where we grab the samples and interleave them. + + unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFrame.header.channelAssignment); + uint64_t totalSamplesInFrame = pFlac->currentFrame.header.blockSize * channelCount; + uint64_t samplesReadFromFrameSoFar = totalSamplesInFrame - pFlac->currentFrame.samplesRemaining; + + int misalignedSampleCount = samplesReadFromFrameSoFar % channelCount; + if (misalignedSampleCount > 0) { + uint64_t misalignedSamplesRead = drflac__read_s32__misaligned(pFlac, misalignedSampleCount, bufferOut); + samplesRead += misalignedSamplesRead; + samplesReadFromFrameSoFar += misalignedSamplesRead; + bufferOut += misalignedSamplesRead; + samplesToRead -= misalignedSamplesRead; + } + + + uint64_t alignedSampleCountPerChannel = samplesToRead / channelCount; + if (alignedSampleCountPerChannel > pFlac->currentFrame.samplesRemaining / channelCount) { + alignedSampleCountPerChannel = pFlac->currentFrame.samplesRemaining / channelCount; + } + + uint64_t firstAlignedSampleInFrame = samplesReadFromFrameSoFar / channelCount; + unsigned int unusedBitsPerSample = 32 - pFlac->bitsPerSample; + + switch (pFlac->currentFrame.header.channelAssignment) + { + case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + { + const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + int left = pDecodedSamples0[i]; + int side = pDecodedSamples1[i]; + int right = left - side; + + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + { + const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples0[i]; + int right = pDecodedSamples1[i]; + int left = right + side; + + bufferOut[i*2+0] = left << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = right << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + { + const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + int side = pDecodedSamples1[i]; + int mid = (((uint32_t)pDecodedSamples0[i]) << 1) | (side & 0x01); + + bufferOut[i*2+0] = ((mid + side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = ((mid - side) >> 1) << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } break; + + case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + default: + { + if (pFlac->currentFrame.header.channelAssignment == 1) // 1 = Stereo + { + // Stereo optimized inner loop unroll. + const int* pDecodedSamples0 = pFlac->currentFrame.subframes[0].pDecodedSamples + firstAlignedSampleInFrame; + const int* pDecodedSamples1 = pFlac->currentFrame.subframes[1].pDecodedSamples + firstAlignedSampleInFrame; + + for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + bufferOut[i*2+0] = pDecodedSamples0[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[0].wastedBitsPerSample); + bufferOut[i*2+1] = pDecodedSamples1[i] << (unusedBitsPerSample + pFlac->currentFrame.subframes[1].wastedBitsPerSample); + } + } + else + { + // Generic interleaving. + for (uint64_t i = 0; i < alignedSampleCountPerChannel; ++i) { + for (unsigned int j = 0; j < channelCount; ++j) { + bufferOut[(i*channelCount)+j] = (pFlac->currentFrame.subframes[j].pDecodedSamples[firstAlignedSampleInFrame + i]) << (unusedBitsPerSample + pFlac->currentFrame.subframes[j].wastedBitsPerSample); + } + } + } + } break; + } + + uint64_t alignedSamplesRead = alignedSampleCountPerChannel * channelCount; + samplesRead += alignedSamplesRead; + samplesReadFromFrameSoFar += alignedSamplesRead; + bufferOut += alignedSamplesRead; + samplesToRead -= alignedSamplesRead; + pFlac->currentFrame.samplesRemaining -= (unsigned int)alignedSamplesRead; + + + + // At this point we may still have some excess samples left to read. + if (samplesToRead > 0 && pFlac->currentFrame.samplesRemaining > 0) + { + uint64_t excessSamplesRead = 0; + if (samplesToRead < pFlac->currentFrame.samplesRemaining) { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, samplesToRead, bufferOut); + } else { + excessSamplesRead = drflac__read_s32__misaligned(pFlac, pFlac->currentFrame.samplesRemaining, bufferOut); + } + + samplesRead += excessSamplesRead; + samplesReadFromFrameSoFar += excessSamplesRead; + bufferOut += excessSamplesRead; + samplesToRead -= excessSamplesRead; + } + } + } + + return samplesRead; +} + +drBool32 drflac_seek_to_sample(drflac* pFlac, uint64_t sampleIndex) +{ + if (pFlac == NULL) { + return DR_FALSE; + } + + if (sampleIndex == 0) { + return drflac__seek_to_first_frame(pFlac); + } + + // Clamp the sample to the end. + if (sampleIndex >= pFlac->totalSampleCount) { + sampleIndex = pFlac->totalSampleCount - 1; + } + + + // 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) + { + return drflac_ogg__seek_to_sample(pFlac, sampleIndex); + } + else +#endif + { + // First try seeking via the seek table. If this fails, fall back to a brute force seek which is much slower. + if (!drflac__seek_to_sample__seek_table(pFlac, sampleIndex)) { + return drflac__seek_to_sample__brute_force(pFlac, sampleIndex); + } + } + + + return DR_TRUE; +} + + + +//// High Level APIs //// + +int32_t* drflac__full_decode_and_close(drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, uint64_t* totalSampleCountOut) +{ + assert(pFlac != NULL); + + int32_t* pSampleData = NULL; + uint64_t totalSampleCount = pFlac->totalSampleCount; + + if (totalSampleCount == 0) + { + int32_t buffer[4096]; + + size_t sampleDataBufferSize = sizeof(buffer); + pSampleData = (int32_t*)malloc(sampleDataBufferSize); + if (pSampleData == NULL) { + goto on_error; + } + + uint64_t samplesRead; + while ((samplesRead = (uint64_t)drflac_read_s32(pFlac, sizeof(buffer)/sizeof(buffer[0]), buffer)) > 0) + { + if (((totalSampleCount + samplesRead) * sizeof(int32_t)) > sampleDataBufferSize) { + sampleDataBufferSize *= 2; + int32_t* pNewSampleData = (int32_t*)realloc(pSampleData, sampleDataBufferSize); + if (pNewSampleData == NULL) { + free(pSampleData); + goto on_error; + } + + pSampleData = pNewSampleData; + } + + memcpy(pSampleData + totalSampleCount, buffer, (size_t)(samplesRead*sizeof(int32_t))); + totalSampleCount += samplesRead; + } + + // At this point everything should be decoded, but we just want to fill the unused part buffer with silence - need to + // protect those ears from random noise! + memset(pSampleData + totalSampleCount, 0, (size_t)(sampleDataBufferSize - totalSampleCount*sizeof(int32_t))); + } + else + { + uint64_t dataSize = totalSampleCount * sizeof(int32_t); + if (dataSize > SIZE_MAX) { + goto on_error; // The decoded data is too big. + } + + pSampleData = (int32_t*)malloc((size_t)dataSize); // <-- Safe cast as per the check above. + if (pSampleData == NULL) { + goto on_error; + } + + uint64_t samplesDecoded = drflac_read_s32(pFlac, pFlac->totalSampleCount, pSampleData); + if (samplesDecoded != pFlac->totalSampleCount) { + free(pSampleData); + goto on_error; // Something went wrong when decoding the FLAC stream. + } + } + + + if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; + if (channelsOut) *channelsOut = pFlac->channels; + if (totalSampleCountOut) *totalSampleCountOut = totalSampleCount; + + drflac_close(pFlac); + return pSampleData; + +on_error: + drflac_close(pFlac); + return NULL; +} + +int32_t* drflac_open_and_decode_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +{ + // Safety. + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open(onRead, onSeek, pUserData); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount); +} + +#ifndef DR_FLAC_NO_STDIO +int32_t* drflac_open_and_decode_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_file(filename); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount); +} +#endif + +int32_t* drflac_open_and_decode_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) +{ + if (sampleRate) *sampleRate = 0; + if (channels) *channels = 0; + if (totalSampleCount) *totalSampleCount = 0; + + drflac* pFlac = drflac_open_memory(data, dataSize); + if (pFlac == NULL) { + return NULL; + } + + return drflac__full_decode_and_close(pFlac, channels, sampleRate, totalSampleCount); +} + +void drflac_free(void* pSampleDataReturnedByOpenAndDecode) +{ + free(pSampleDataReturnedByOpenAndDecode); +} + + + + +void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, uint32_t commentCount, const char* pComments) +{ + if (pIter == NULL) { + return; + } + + pIter->countRemaining = commentCount; + pIter->pRunningData = pComments; +} + +const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, uint32_t* pCommentLengthOut) +{ + // Safety. + if (pCommentLengthOut) *pCommentLengthOut = 0; + + if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { + return NULL; + } + + uint32_t length = drflac__le2host_32(*(uint32_t*)pIter->pRunningData); + pIter->pRunningData += 4; + + const char* pComment = pIter->pRunningData; + pIter->pRunningData += length; + pIter->countRemaining -= 1; + + if (pCommentLengthOut) *pCommentLengthOut = length; + return pComment; +} +#endif //DR_FLAC_IMPLEMENTATION + + +// REVISION HISTORY +// +// v0.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 dr_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. + + +// TODO +// - Add support for initializing the decoder without a header STREAMINFO block. +// - Test CUESHEET metadata blocks. + + +/* +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 +*/ -- cgit v1.2.3 From 1c05017548ea21dd1a44c31e9fc80b8700891f11 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 15 Oct 2016 13:51:33 +0200 Subject: Added OpenAL static library --- src/external/openal_soft/include/AL/alext.h | 5 +++++ src/external/openal_soft/lib/win32/libOpenAL32.a | Bin 0 -> 2226842 bytes 2 files changed, 5 insertions(+) create mode 100644 src/external/openal_soft/lib/win32/libOpenAL32.a (limited to 'src/external') diff --git a/src/external/openal_soft/include/AL/alext.h b/src/external/openal_soft/include/AL/alext.h index 6af581aa..0090c804 100644 --- a/src/external/openal_soft/include/AL/alext.h +++ b/src/external/openal_soft/include/AL/alext.h @@ -431,6 +431,11 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi #endif #endif +#ifndef AL_SOFT_gain_clamp_ex +#define AL_SOFT_gain_clamp_ex 1 +#define AL_GAIN_LIMIT_SOFT 0x200E +#endif + #ifdef __cplusplus } #endif diff --git a/src/external/openal_soft/lib/win32/libOpenAL32.a b/src/external/openal_soft/lib/win32/libOpenAL32.a new file mode 100644 index 00000000..3c0df3c7 Binary files /dev/null and b/src/external/openal_soft/lib/win32/libOpenAL32.a differ -- cgit v1.2.3