aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorvictorfisac <victorfisac@gmail.com>2016-11-01 23:11:36 +0100
committervictorfisac <victorfisac@gmail.com>2016-11-01 23:11:36 +0100
commit80f6b2f9635d8e4707b528337c39c0ba7bf9cf5f (patch)
treec9587d0b1e84a89a3e9c8acb1ae16a3aeede4f78 /src
parent2a158c47955d2d849e939152e0c174b02c500104 (diff)
parent64f67f6e9f414a54dfc3fb519b892ecd5517f2cf (diff)
downloadraylib-80f6b2f9635d8e4707b528337c39c0ba7bf9cf5f.tar.gz
raylib-80f6b2f9635d8e4707b528337c39c0ba7bf9cf5f.zip
Merge remote-tracking branch 'refs/remotes/raysan5/develop' into develop
Diffstat (limited to 'src')
-rw-r--r--src/audio.c64
-rw-r--r--src/core.c439
-rw-r--r--src/external/dr_flac.h4395
-rw-r--r--src/external/openal_soft/include/AL/alext.h5
-rw-r--r--src/external/openal_soft/lib/win32/libOpenAL32.abin0 -> 2226842 bytes
-rw-r--r--src/external/openal_soft/lib/win32/libOpenAL32dll.a (renamed from src/external/openal_soft/lib/win32/libOpenAL32.dll.a)bin100246 -> 100246 bytes
-rw-r--r--src/models.c124
-rw-r--r--src/raylib.h140
-rw-r--r--src/rlgl.c115
-rw-r--r--src/rlgl.h42
-rw-r--r--src/rlua.h83
-rw-r--r--src/shader_standard.h10
-rw-r--r--src/shapes.c43
-rw-r--r--src/text.c212
-rw-r--r--src/textures.c145
-rw-r--r--src/utils.c2
16 files changed, 5359 insertions, 460 deletions
diff --git a/src/audio.c b/src/audio.c
index 90bf4968..3684e10a 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 = (unsigned int)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);
@@ -819,6 +845,20 @@ void UpdateMusicStream(Music music)
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)
+ unsigned int numSamplesFlac = (unsigned int)drflac_read_s32(music->ctxFlac, numSamples, pcmi);
+
+ UpdateAudioStream(music->stream, pcmi, numSamplesFlac*music->stream.channels);
+ music->samplesLeft -= (numSamples*music->stream.channels);
+
+ } break;
case MUSIC_MODULE_XM:
{
if (music->samplesLeft >= AUDIO_BUFFER_SIZE/2) numSamples = AUDIO_BUFFER_SIZE/2;
@@ -1197,7 +1237,7 @@ static Wave LoadOGG(const char *fileName)
if (totalSeconds > 10) TraceLog(WARNING, "[%s] Ogg audio lenght is larger than 10 seconds (%f), that's a big file in memory, consider music streaming", fileName, totalSeconds);
- int totalSamples = totalSeconds*info.sample_rate*info.channels;
+ int totalSamples = (int)(totalSeconds*info.sample_rate*info.channels);
wave.sampleCount = totalSamples;
wave.data = (short *)malloc(totalSamplesLength*sizeof(short));
@@ -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/core.c b/src/core.c
index 0db1c573..3bade07b 100644
--- a/src/core.c
+++ b/src/core.c
@@ -18,7 +18,11 @@
*
* On PLATFORM_RPI, graphic device is managed by EGL and input system is coded in raw mode.
*
-* Copyright (c) 2014 Ramon Santamaria (@raysan5)
+* Module Configuration Flags:
+*
+* RL_LOAD_DEFAULT_FONT - Use external module functions to load default raylib font (module: text)
+*
+* Copyright (c) 2014-2016 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.
@@ -54,12 +58,12 @@
#endif
#include <stdio.h> // Standard input / output lib
-#include <stdlib.h> // Declares malloc() and free() for memory management, rand(), atexit()
-#include <stdint.h> // Required for typedef unsigned long long int uint64_t, used by hi-res timer
-#include <time.h> // Useful to initialize random seed - Android/RPI hi-res timer (NOTE: Linux only!)
-#include <math.h> // Math related functions, tan() used to set perspective
-#include <string.h> // String function definitions, memset()
-#include <errno.h> // Macros for reporting and retrieving error conditions through error codes
+#include <stdlib.h> // Required for: malloc(), free(), rand(), atexit()
+#include <stdint.h> // Required for: typedef unsigned long long int uint64_t, used by hi-res timer
+#include <time.h> // Required for: time() - Android/RPI hi-res timer (NOTE: Linux only!)
+#include <math.h> // Required for: tan() [Used in Begin3dMode() to set perspective]
+#include <string.h> // Required for: strcmp()
+//#include <errno.h> // Macros for reporting and retrieving error conditions through error codes
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
//#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3
@@ -75,8 +79,7 @@
#endif
#if defined(PLATFORM_ANDROID)
- #include <jni.h> // Java native interface
- #include <android/sensor.h> // Android sensors functions
+ //#include <android/sensor.h> // Android sensors functions (accelerometer, gyroscope, light...)
#include <android/window.h> // Defines AWINDOW_FLAG_FULLSCREEN and others
#include <android_native_app_glue.h> // Defines basic app state struct and manages activity
@@ -124,12 +127,14 @@
//#define DEFAULT_GAMEPAD_DEV "/dev/input/eventN"
#define MOUSE_SENSITIVITY 0.8f
-
- #define MAX_GAMEPADS 2 // Max number of gamepads supported
- #define MAX_GAMEPAD_BUTTONS 11 // Max bumber of buttons supported (per gamepad)
- #define MAX_GAMEPAD_AXIS 8 // Max number of axis supported (per gamepad)
#endif
+#define MAX_GAMEPADS 4 // Max number of gamepads supported
+#define MAX_GAMEPAD_BUTTONS 32 // Max bumber of buttons supported (per gamepad)
+#define MAX_GAMEPAD_AXIS 8 // Max number of axis supported (per gamepad)
+
+#define RL_LOAD_DEFAULT_FONT // Load default font on window initialization (module: text)
+
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
@@ -152,9 +157,6 @@ static const char *internalDataPath; // Android internal data path to
static bool windowReady = false; // Used to detect display initialization
static bool appEnabled = true; // Used to detec if app is active
static bool contextRebindRequired = false; // Used to know context rebind required
-
-static int previousButtonState[128] = { 1 }; // Required to check if button pressed/released once
-static int currentButtonState[128] = { 1 }; // Required to check if button pressed/released once
#endif
#if defined(PLATFORM_RPI)
@@ -168,15 +170,11 @@ static int defaultKeyboardMode; // Used to store default keyboar
// Mouse input variables
static int mouseStream = -1; // Mouse device file descriptor
static bool mouseReady = false; // Flag to know if mouse is ready
-pthread_t mouseThreadId; // Mouse reading thread id
+static pthread_t mouseThreadId; // Mouse reading thread id
// Gamepad input variables
-static int gamepadStream[MAX_GAMEPADS] = { -1 }; // Gamepad device file descriptor (two gamepads supported)
-static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready (two gamepads supported)
-pthread_t gamepadThreadId; // Gamepad reading thread id
-
-int gamepadButtons[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Gamepad buttons state
-float gamepadAxisValues[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
+static int gamepadStream[MAX_GAMEPADS] = { -1 };// Gamepad device file descriptor
+static pthread_t gamepadThreadId; // Gamepad reading thread id
#endif
#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
@@ -200,24 +198,31 @@ static Matrix downscaleView; // Matrix to downscale view (in case
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
static const char *windowTitle; // Window text title...
static bool cursorOnScreen = false; // Tracks if cursor is inside client area
+static bool cursorHidden = false; // Track if cursor is hidden
-static char previousKeyState[512] = { 0 }; // Required to check if key pressed/released once
-static char currentKeyState[512] = { 0 }; // Required to check if key pressed/released once
+// Register mouse states
+static char previousMouseState[3] = { 0 }; // Registers previous mouse button state
+static char currentMouseState[3] = { 0 }; // Registers current mouse button state
+static int previousMouseWheelY = 0; // Registers previous mouse wheel variation
+static int currentMouseWheelY = 0; // Registers current mouse wheel variation
-static char previousGamepadState[32] = {0}; // Required to check if gamepad btn pressed/released once
-static char currentGamepadState[32] = {0}; // Required to check if gamepad btn pressed/released once
+// Register gamepads states
+static bool gamepadReady[MAX_GAMEPADS] = { false }; // Flag to know if gamepad is ready
+static float gamepadAxisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state
+static char previousGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state
+static char currentGamepadState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state
-static char previousMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
-static char currentMouseState[3] = { 0 }; // Required to check if mouse btn pressed/released once
+// Keyboard configuration
+static int exitKey = KEY_ESCAPE; // Default exit key (ESC)
+#endif
-static int previousMouseWheelY = 0; // Required to track mouse wheel variation
-static int currentMouseWheelY = 0; // Required to track mouse wheel variation
+// Register keyboard states
+static char previousKeyState[512] = { 0 }; // Registers previous frame key state
+static char currentKeyState[512] = { 0 }; // Registers current frame key state
-static int exitKey = KEY_ESCAPE; // Default exit key (ESC)
static int lastKeyPressed = -1; // Register last key pressed
-
-static bool cursorHidden; // Track if cursor is hidden
-#endif
+static int lastGamepadButtonPressed = -1; // Register last gamepad button pressed
+static int gamepadAxisCount = 0; // Register number of available gamepad axis
static Vector2 mousePosition; // Mouse position on screen
static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch position on screen
@@ -238,11 +243,10 @@ static bool showLogo = false; // Track if showing logo at init is
//----------------------------------------------------------------------------------
// Other Modules Functions Declaration (required by core)
//----------------------------------------------------------------------------------
+#if defined(RL_LOAD_DEFAULT_FONT)
extern void LoadDefaultFont(void); // [Module: text] Loads default font on InitWindow()
extern void UnloadDefaultFont(void); // [Module: text] Unloads default font from GPU memory
-
-extern void ProcessGestureEvent(GestureEvent event); // [Module: gestures] Process gesture event and translate it into gestures
-extern void UpdateGestures(void); // [Module: gestures] Update gestures detected (called in PollInputEvents())
+#endif
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
@@ -311,9 +315,11 @@ void InitWindow(int width, int height, const char *title)
// Init graphics device (display device and OpenGL context)
InitGraphicsDevice(width, height);
- // Load default font for convenience
+#if defined(RL_LOAD_DEFAULT_FONT)
+ // Load default font
// NOTE: External function (defined in module: text)
LoadDefaultFont();
+#endif
// Init hi-res timer
InitTimer();
@@ -355,7 +361,7 @@ void InitWindow(int width, int height, const char *title)
#if defined(PLATFORM_ANDROID)
// Android activity initialization
-void InitWindow(int width, int height, struct android_app *state)
+void InitWindow(int width, int height, void *state)
{
TraceLog(INFO, "Initializing raylib (v1.6.0)");
@@ -364,7 +370,7 @@ void InitWindow(int width, int height, struct android_app *state)
screenWidth = width;
screenHeight = height;
- app = state;
+ app = (struct android_app *)state;
internalDataPath = app->activity->internalDataPath;
// Set desired windows flags before initializing anything
@@ -420,7 +426,9 @@ void InitWindow(int width, int height, struct android_app *state)
// Close Window and Terminate Context
void CloseWindow(void)
{
+#if defined(RL_LOAD_DEFAULT_FONT)
UnloadDefaultFont();
+#endif
rlglClose(); // De-init rlgl
@@ -456,6 +464,9 @@ void CloseWindow(void)
// Wait for mouse and gamepad threads to finish before closing
// NOTE: Those threads should already have finished at this point
// because they are controlled by windowShouldClose variable
+
+ windowShouldClose = true; // Added to force threads to exit when the close window is called
+
pthread_join(mouseThreadId, NULL);
pthread_join(gamepadThreadId, NULL);
#endif
@@ -516,6 +527,65 @@ int GetScreenHeight(void)
return screenHeight;
}
+#if !defined(PLATFORM_ANDROID)
+// Show mouse cursor
+void ShowCursor()
+{
+#if defined(PLATFORM_DESKTOP)
+ #ifdef __linux
+ XUndefineCursor(glfwGetX11Display(), glfwGetX11Window(window));
+ #else
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ #endif
+#endif
+ cursorHidden = false;
+}
+
+// Hide mouse cursor
+void HideCursor()
+{
+#if defined(PLATFORM_DESKTOP)
+ #ifdef __linux
+ XColor Col;
+ const char Nil[] = {0};
+
+ Pixmap Pix = XCreateBitmapFromData(glfwGetX11Display(), glfwGetX11Window(window), Nil, 1, 1);
+ Cursor Cur = XCreatePixmapCursor(glfwGetX11Display(), Pix, Pix, &Col, &Col, 0, 0);
+
+ XDefineCursor(glfwGetX11Display(), glfwGetX11Window(window), Cur);
+ XFreeCursor(glfwGetX11Display(), Cur);
+ #else
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
+ #endif
+#endif
+ cursorHidden = true;
+}
+
+// Check if mouse cursor is hidden
+bool IsCursorHidden()
+{
+ return cursorHidden;
+}
+
+// Enable mouse cursor
+void EnableCursor()
+{
+#if defined(PLATFORM_DESKTOP)
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+#endif
+ cursorHidden = false;
+}
+
+// Disable mouse cursor
+void DisableCursor()
+{
+#if defined(PLATFORM_DESKTOP)
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+#endif
+ cursorHidden = true;
+}
+#endif // !defined(PLATFORM_ANDROID)
+
// Sets Background Color
void ClearBackground(Color color)
{
@@ -1034,7 +1104,6 @@ Matrix GetCameraMatrix(Camera camera)
//----------------------------------------------------------------------------------
// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
//----------------------------------------------------------------------------------
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
// Detect if a key has been pressed once
bool IsKeyPressed(int key)
{
@@ -1057,7 +1126,7 @@ bool IsKeyDown(int key)
bool IsKeyReleased(int key)
{
bool released = false;
-
+
if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 0)) released = true;
else released = false;
@@ -1081,99 +1150,62 @@ int GetKeyPressed(void)
// NOTE: default exitKey is ESCAPE
void SetExitKey(int key)
{
+#if !defined(PLATFORM_ANDROID)
exitKey = key;
-}
-
-// Hide mouse cursor
-void HideCursor()
-{
-#if defined(PLATFORM_DESKTOP)
- #ifdef __linux
- XColor Col;
- const char Nil[] = {0};
-
- Pixmap Pix = XCreateBitmapFromData(glfwGetX11Display(), glfwGetX11Window(window), Nil, 1, 1);
- Cursor Cur = XCreatePixmapCursor(glfwGetX11Display(), Pix, Pix, &Col, &Col, 0, 0);
-
- XDefineCursor(glfwGetX11Display(), glfwGetX11Window(window), Cur);
- XFreeCursor(glfwGetX11Display(), Cur);
- #else
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
- #endif
#endif
- cursorHidden = true;
}
-// Show mouse cursor
-void ShowCursor()
-{
-#if defined(PLATFORM_DESKTOP)
- #ifdef __linux
- XUndefineCursor(glfwGetX11Display(), glfwGetX11Window(window));
- #else
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
- #endif
-#endif
- cursorHidden = false;
-}
+// NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB)
-// Disable mouse cursor
-void DisableCursor()
+// Detect if a gamepad is available
+bool IsGamepadAvailable(int gamepad)
{
-#if defined(PLATFORM_DESKTOP)
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
+ bool result = false;
+
+#if !defined(PLATFORM_ANDROID)
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad]) result = true;
#endif
- cursorHidden = true;
-}
-// Enable mouse cursor
-void EnableCursor()
-{
-#if defined(PLATFORM_DESKTOP)
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
-#endif
- cursorHidden = false;
+ return result;
}
-// Check if mouse cursor is hidden
-bool IsCursorHidden()
+// Check gamepad name (if available)
+bool IsGamepadName(int gamepad, const char *name)
{
- return cursorHidden;
+ bool result = false;
+ const char *gamepadName = NULL;
+
+ if (gamepadReady[gamepad]) gamepadName = GetGamepadName(gamepad);
+
+ if ((name != NULL) && (gamepadName != NULL)) result = (strcmp(name, gamepadName) == 0);
+
+ return result;
}
-// NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB)
-
-// Detect if a gamepad is available
-bool IsGamepadAvailable(int gamepad)
+// Return gamepad internal name id
+const char *GetGamepadName(int gamepad)
{
- bool result = false;
-
-#if defined(PLATFORM_RPI)
- if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad]) result = true;
+#if defined(PLATFORM_DESKTOP)
+ if (gamepadReady[gamepad]) return glfwGetJoystickName(gamepad);
+ else return NULL;
#else
- if (glfwJoystickPresent(gamepad) == 1) result = true;
+ return NULL;
#endif
+}
- return result;
+// Return gamepad axis count
+int GetGamepadAxisCount(int gamepad)
+{
+ return gamepadAxisCount;
}
// Return axis movement vector for a gamepad
float GetGamepadAxisMovement(int gamepad, int axis)
{
float value = 0;
-
-#if defined(PLATFORM_RPI)
- if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad])
- {
- if (axis < MAX_GAMEPAD_AXIS) value = gamepadAxisValues[gamepad][axis];
- }
-#else
- const float *axes;
- int axisCount = 0;
-
- axes = glfwGetJoystickAxes(gamepad, &axisCount);
-
- if (axis < axisCount) value = axes[axis];
+
+#if !defined(PLATFORM_ANDROID)
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (axis < MAX_GAMEPAD_AXIS)) value = gamepadAxisState[gamepad][axis];
#endif
return value;
@@ -1184,14 +1216,11 @@ bool IsGamepadButtonPressed(int gamepad, int button)
{
bool pressed = false;
- currentGamepadState[button] = IsGamepadButtonDown(gamepad, button);
-
- if (currentGamepadState[button] != previousGamepadState[button])
- {
- if (currentGamepadState[button]) pressed = true;
- previousGamepadState[button] = currentGamepadState[button];
- }
- else pressed = false;
+#if !defined(PLATFORM_ANDROID)
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) &&
+ (currentGamepadState[gamepad][button] == 1)) pressed = true;
+#endif
return pressed;
}
@@ -1201,18 +1230,9 @@ bool IsGamepadButtonDown(int gamepad, int button)
{
bool result = false;
-#if defined(PLATFORM_RPI)
- // Get gamepad buttons information
- if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (gamepadButtons[gamepad][button] == 1)) result = true;
- else result = false;
-#else
- const unsigned char *buttons;
- int buttonsCount;
-
- buttons = glfwGetJoystickButtons(gamepad, &buttonsCount);
-
- if ((buttons != NULL) && (buttons[button] == GLFW_PRESS)) result = true;
- else result = false;
+#if !defined(PLATFORM_ANDROID)
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] == 1)) result = true;
#endif
return result;
@@ -1222,15 +1242,12 @@ bool IsGamepadButtonDown(int gamepad, int button)
bool IsGamepadButtonReleased(int gamepad, int button)
{
bool released = false;
-
- currentGamepadState[button] = IsGamepadButtonUp(gamepad, button);
-
- if (currentGamepadState[button] != previousGamepadState[button])
- {
- if (currentGamepadState[button]) released = true;
- previousGamepadState[button] = currentGamepadState[button];
- }
- else released = false;
+
+#if !defined(PLATFORM_ANDROID)
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] != previousGamepadState[gamepad][button]) &&
+ (currentGamepadState[gamepad][button] == 0)) released = true;
+#endif
return released;
}
@@ -1240,24 +1257,19 @@ bool IsGamepadButtonUp(int gamepad, int button)
{
bool result = false;
-#if defined(PLATFORM_RPI)
- // Get gamepad buttons information
- if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (gamepadButtons[gamepad][button] == 0)) result = true;
- else result = false;
-#else
- const unsigned char *buttons;
- int buttonsCount;
-
- buttons = glfwGetJoystickButtons(gamepad, &buttonsCount);
-
- if ((buttons != NULL) && (buttons[button] == GLFW_RELEASE)) result = true;
- else result = false;
+#if !defined(PLATFORM_ANDROID)
+ if ((gamepad < MAX_GAMEPADS) && gamepadReady[gamepad] && (button < MAX_GAMEPAD_BUTTONS) &&
+ (currentGamepadState[gamepad][button] == 0)) result = true;
#endif
return result;
}
-#endif //defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
+// Get the last gamepad button pressed
+int GetGamepadButtonPressed(void)
+{
+ return lastGamepadButtonPressed;
+}
// Detect if a mouse button has been pressed once
bool IsMouseButtonPressed(int button)
@@ -1411,37 +1423,6 @@ Vector2 GetTouchPosition(int index)
return position;
}
-#if defined(PLATFORM_ANDROID)
-// Detect if a button has been pressed once
-bool IsButtonPressed(int button)
-{
- bool pressed = false;
-
- if ((currentButtonState[button] != previousButtonState[button]) && (currentButtonState[button] == 0)) pressed = true;
- else pressed = false;
-
- return pressed;
-}
-
-// Detect if a button is being pressed (button held down)
-bool IsButtonDown(int button)
-{
- if (currentButtonState[button] == 0) return true;
- else return false;
-}
-
-// Detect if a button has been released once
-bool IsButtonReleased(int button)
-{
- bool released = false;
-
- if ((currentButtonState[button] != previousButtonState[button]) && (currentButtonState[button] == 1)) released = true;
- else released = false;
-
- return released;
-}
-#endif
-
//----------------------------------------------------------------------------------
// Module specific Functions Definition
//----------------------------------------------------------------------------------
@@ -1924,8 +1905,9 @@ static bool GetKeyStatus(int key)
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
return glfwGetKey(window, key);
#elif defined(PLATFORM_ANDROID)
- // TODO: Check for virtual keyboard
- return false;
+ // NOTE: Android supports up to 260 keys
+ if (key < 0 || key > 260) return false;
+ else return currentKeyState[key];
#elif defined(PLATFORM_RPI)
// NOTE: Keys states are filled in PollInputEvents()
if (key < 0 || key > 511) return false;
@@ -1953,6 +1935,13 @@ static void PollInputEvents(void)
// NOTE: Gestures update must be called every frame to reset gestures correctly
// because ProcessGestureEvent() is just called on an event, not every frame
UpdateGestures();
+
+ // Reset last key pressed registered
+ lastKeyPressed = -1;
+
+ // Reset last gamepad button pressed registered
+ lastGamepadButtonPressed = -1;
+ gamepadAxisCount = 0;
#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
// Mouse input polling
@@ -1963,9 +1952,8 @@ static void PollInputEvents(void)
mousePosition.x = (float)mouseX;
mousePosition.y = (float)mouseY;
-
+
// Keyboard input polling (automatically managed by GLFW3 through callback)
- lastKeyPressed = -1;
// Register previous keys states
for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
@@ -1975,13 +1963,62 @@ static void PollInputEvents(void)
previousMouseWheelY = currentMouseWheelY;
currentMouseWheelY = 0;
+
+ // Check if gamepads are ready
+ // NOTE: We do it here in case of disconection
+ for (int i = 0; i < MAX_GAMEPADS; i++)
+ {
+ if (glfwJoystickPresent(i)) gamepadReady[i] = true;
+ else gamepadReady[i] = false;
+ }
+
+ // Register gamepads buttons events
+ for (int i = 0; i < MAX_GAMEPADS; i++)
+ {
+ if (gamepadReady[i]) // Check if gamepad is available
+ {
+ // Register previous gamepad states
+ for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) previousGamepadState[i][k] = currentGamepadState[i][k];
+
+ // Get current gamepad state
+ // NOTE: There is no callback available, so we get it manually
+ const unsigned char *buttons;
+ int buttonsCount;
+
+ buttons = glfwGetJoystickButtons(i, &buttonsCount);
+
+ for (int k = 0; (buttons != NULL) && (k < buttonsCount) && (buttonsCount < MAX_GAMEPAD_BUTTONS); k++)
+ {
+ if (buttons[k] == GLFW_PRESS)
+ {
+ currentGamepadState[i][k] = 1;
+ lastGamepadButtonPressed = k;
+ }
+ else currentGamepadState[i][k] = 0;
+ }
+
+ // Get current axis state
+ const float *axes;
+ int axisCount = 0;
+
+ axes = glfwGetJoystickAxes(i, &axisCount);
+
+ for (int k = 0; (axes != NULL) && (k < axisCount) && (k < MAX_GAMEPAD_AXIS); k++)
+ {
+ gamepadAxisState[i][k] = axes[k];
+ }
+
+ gamepadAxisCount = axisCount;
+ }
+ }
- glfwPollEvents(); // Register keyboard/mouse events... and window events!
+ glfwPollEvents(); // Register keyboard/mouse events (callbacks)... and window events!
#endif
#if defined(PLATFORM_ANDROID)
// Register previous keys states
- for (int i = 0; i < 128; i++) previousButtonState[i] = currentButtonState[i];
+ // NOTE: Android supports up to 260 keys
+ for (int i = 0; i < 260; i++) previousKeyState[i] = currentKeyState[i];
// Poll Events (registered events)
// NOTE: Activity is paused if not enabled (appEnabled)
@@ -2245,9 +2282,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd)
// Init graphics device (display device and OpenGL context)
InitGraphicsDevice(screenWidth, screenHeight);
- // Load default font for convenience
+ #if defined(RL_LOAD_DEFAULT_FONT)
+ // Load default font
// NOTE: External function (defined in module: text)
LoadDefaultFont();
+ #endif
// TODO: GPU assets reload in case of lost focus (lost context)
// NOTE: This problem has been solved just unbinding and rebinding context from display
@@ -2356,7 +2395,13 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event)
//int32_t AKeyEvent_getMetaState(event);
// Save current button and its state
- currentButtonState[keycode] = AKeyEvent_getAction(event); // Down = 0, Up = 1
+ // NOTE: Android key action is 0 for down and 1 for up
+ if (AKeyEvent_getAction(event) == 0)
+ {
+ currentKeyState[keycode] = 1; // Key down
+ lastKeyPressed = keycode;
+ }
+ else currentKeyState[keycode] = 0; // Key up
if (keycode == AKEYCODE_POWER)
{
@@ -2802,7 +2847,9 @@ static void *GamepadThread(void *arg)
if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
{
// 1 - button pressed, 0 - button released
- gamepadButtons[i][gamepadEvent.number] = (int)gamepadEvent.value;
+ currentGamepadState[i][gamepadEvent.number] = (int)gamepadEvent.value;
+
+ if ((int)gamepadEvent.value == 1) lastGamepadButtonPressed = gamepadEvent.number;
}
}
else if (gamepadEvent.type == JS_EVENT_AXIS)
@@ -2812,7 +2859,7 @@ static void *GamepadThread(void *arg)
if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
{
// NOTE: Scaling of gamepadEvent.value to get values between -1..1
- gamepadAxisValues[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
+ gamepadAxisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768;
}
}
}
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 <number>
+// Defines the size of the internal buffer to store data from onRead(). This buffer is used to reduce the number of calls
+// back to the client for more data. Larger values means more memory, but better performance. My tests show diminishing
+// returns after about 4KB (which is the default). Consider reducing this if you have a very efficient implementation of
+// onRead(), or increase it if it's very inefficient. Must be a multiple of 8.
+//
+//
+//
+// 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 <stdint.h>
+#include <stddef.h>
+
+#ifndef DR_BOOL_DEFINED
+#define DR_BOOL_DEFINED
+#ifdef _WIN32
+typedef char drBool8;
+typedef int drBool32;
+#else
+#include <stdint.h>
+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 <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifdef _MSC_VER
+#include <intrin.h> // For _byteswap_ulong and _byteswap_uint64
+#endif
+
+#ifdef __linux__
+#define _BSD_SOURCE
+#include <endian.h>
+#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 <order> residuals will be ignored. The
+// <blockSize> and <order> 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 <order> 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 <order> residuals will be set to 0. The
+// <blockSize> and <order> parameters are used to determine how many residual values need to be decoded.
+static 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 <stdio.h>
+
+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 <windows.h>
+
+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 <bufferOut> 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 <http://unlicense.org/>
+*/
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
--- /dev/null
+++ b/src/external/openal_soft/lib/win32/libOpenAL32.a
Binary files differ
diff --git a/src/external/openal_soft/lib/win32/libOpenAL32.dll.a b/src/external/openal_soft/lib/win32/libOpenAL32dll.a
index 1c4c63c8..1c4c63c8 100644
--- a/src/external/openal_soft/lib/win32/libOpenAL32.dll.a
+++ b/src/external/openal_soft/lib/win32/libOpenAL32dll.a
Binary files differ
diff --git a/src/models.c b/src/models.c
index 822da6e9..c0f04387 100644
--- a/src/models.c
+++ b/src/models.c
@@ -4,7 +4,7 @@
*
* Basic functions to draw 3d shapes and load/draw 3d models (.OBJ)
*
-* Copyright (c) 2014 Ramon Santamaria (@raysan5)
+* Copyright (c) 2014-2016 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.
@@ -87,8 +87,8 @@ void DrawCircle3D(Vector3 center, float radius, float rotationAngle, Vector3 rot
{
rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex3f(sin(DEG2RAD*i)*radius, cos(DEG2RAD*i)*radius, 0.0f);
- rlVertex3f(sin(DEG2RAD*(i + 10))*radius, cos(DEG2RAD*(i + 10))*radius, 0.0f);
+ rlVertex3f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius, 0.0f);
+ rlVertex3f(sinf(DEG2RAD*(i + 10))*radius, cosf(DEG2RAD*(i + 10))*radius, 0.0f);
}
rlEnd();
rlPopMatrix();
@@ -325,25 +325,25 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
{
for (int j = 0; j < slices; j++)
{
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*(j*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*(j*360/slices)));
-
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i)))*sin(DEG2RAD*((j+1)*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i)))*cos(DEG2RAD*((j+1)*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
+
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*sinf(DEG2RAD*((j+1)*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i)))*cosf(DEG2RAD*((j+1)*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
}
}
rlEnd();
@@ -364,26 +364,26 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col
{
for (int j = 0; j < slices; j++)
{
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
-
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*((j+1)*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*((j+1)*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*(j*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*(j*360/slices)));
-
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sin(DEG2RAD*(j*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
- cos(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cos(DEG2RAD*(j*360/slices)));
- rlVertex3f(cos(DEG2RAD*(270+(180/(rings + 1))*i))*sin(DEG2RAD*(j*360/slices)),
- sin(DEG2RAD*(270+(180/(rings + 1))*i)),
- cos(DEG2RAD*(270+(180/(rings + 1))*i))*cos(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
+
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*((j+1)*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*((j+1)*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
+
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*sinf(DEG2RAD*(j*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*(i+1))),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*(i+1)))*cosf(DEG2RAD*(j*360/slices)));
+ rlVertex3f(cosf(DEG2RAD*(270+(180/(rings + 1))*i))*sinf(DEG2RAD*(j*360/slices)),
+ sinf(DEG2RAD*(270+(180/(rings + 1))*i)),
+ cosf(DEG2RAD*(270+(180/(rings + 1))*i))*cosf(DEG2RAD*(j*360/slices)));
}
}
rlEnd();
@@ -407,21 +407,21 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
// Draw Body -------------------------------------------------------------------------------------
for (int i = 0; i < 360; i += 360/sides)
{
- rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom); //Bottom Left
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom); //Bottom Right
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop); //Top Right
+ rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom); //Bottom Right
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop); //Top Right
- rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop); //Top Left
- rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom); //Bottom Left
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop); //Top Right
+ rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop); //Top Left
+ rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom); //Bottom Left
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop); //Top Right
}
// Draw Cap --------------------------------------------------------------------------------------
for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, height, 0);
- rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop);
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop);
+ rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop);
}
}
else
@@ -430,8 +430,8 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, height, 0);
- rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom);
}
}
@@ -439,8 +439,8 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
for (int i = 0; i < 360; i += 360/sides)
{
rlVertex3f(0, 0, 0);
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
- rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
}
rlEnd();
rlPopMatrix();
@@ -460,17 +460,17 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl
for (int i = 0; i < 360; i += 360/sides)
{
- rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom);
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusBottom, 0, cos(DEG2RAD*(i+360/sides))*radiusBottom);
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop);
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusBottom, 0, cosf(DEG2RAD*(i+360/sides))*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop);
- rlVertex3f(sin(DEG2RAD*(i+360/sides))*radiusTop, height, cos(DEG2RAD*(i+360/sides))*radiusTop);
- rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop);
+ rlVertex3f(sinf(DEG2RAD*(i+360/sides))*radiusTop, height, cosf(DEG2RAD*(i+360/sides))*radiusTop);
+ rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
- rlVertex3f(sin(DEG2RAD*i)*radiusTop, height, cos(DEG2RAD*i)*radiusTop);
- rlVertex3f(sin(DEG2RAD*i)*radiusBottom, 0, cos(DEG2RAD*i)*radiusBottom);
+ rlVertex3f(sinf(DEG2RAD*i)*radiusTop, height, cosf(DEG2RAD*i)*radiusTop);
+ rlVertex3f(sinf(DEG2RAD*i)*radiusBottom, 0, cosf(DEG2RAD*i)*radiusBottom);
}
rlEnd();
rlPopMatrix();
diff --git a/src/raylib.h b/src/raylib.h
index e6e510a9..58037770 100644
--- a/src/raylib.h
+++ b/src/raylib.h
@@ -77,10 +77,6 @@
#define PLATFORM_DESKTOP
#endif
-#if defined(PLATFORM_ANDROID)
- typedef struct android_app; // Define android_app struct (android_native_app_glue.h)
-#endif
-
#if defined(_WIN32) && defined(BUILDING_DLL)
#define RLAPI __declspec(dllexport) // We are building raylib as a Win32 DLL
#elif defined(_WIN32) && defined(RAYLIB_DLL)
@@ -93,7 +89,7 @@
// Some basic Defines
//----------------------------------------------------------------------------------
#ifndef PI
- #define PI 3.14159265358979323846
+ #define PI 3.14159265358979323846f
#endif
#define DEG2RAD (PI/180.0f)
@@ -174,15 +170,18 @@
#define KEY_Y 89
#define KEY_Z 90
+#if defined(PLATFORM_ANDROID)
+ // Android Physical Buttons
+ #define KEY_BACK 4
+ #define KEY_MENU 82
+ #define KEY_VOLUME_UP 24
+ #define KEY_VOLUME_DOWN 25
+#endif
+
// Mouse Buttons
#define MOUSE_LEFT_BUTTON 0
-#if defined(PLATFORM_WEB)
- #define MOUSE_RIGHT_BUTTON 2
- #define MOUSE_MIDDLE_BUTTON 1
-#else
- #define MOUSE_RIGHT_BUTTON 1
- #define MOUSE_MIDDLE_BUTTON 2
-#endif
+#define MOUSE_RIGHT_BUTTON 1
+#define MOUSE_MIDDLE_BUTTON 2
// Touch points registered
#define MAX_TOUCH_POINTS 2
@@ -190,24 +189,35 @@
// Gamepad Number
#define GAMEPAD_PLAYER1 0
#define GAMEPAD_PLAYER2 1
-#define GAMEPAD_PLAYER3 2 // Not supported
-#define GAMEPAD_PLAYER4 3 // Not supported
+#define GAMEPAD_PLAYER3 2
+#define GAMEPAD_PLAYER4 3
-// Gamepad Buttons
+// Gamepad Buttons/Axis
-// PS3 USB Controller
-#define GAMEPAD_PS3_BUTTON_A 2
-#define GAMEPAD_PS3_BUTTON_B 1
-#define GAMEPAD_PS3_BUTTON_X 3
-#define GAMEPAD_PS3_BUTTON_Y 4
+// PS3 USB Controller Buttons
+#define GAMEPAD_PS3_BUTTON_TRIANGLE 0
+#define GAMEPAD_PS3_BUTTON_CIRCLE 1
+#define GAMEPAD_PS3_BUTTON_CROSS 2
+#define GAMEPAD_PS3_BUTTON_SQUARE 3
+#define GAMEPAD_PS3_BUTTON_L1 6
#define GAMEPAD_PS3_BUTTON_R1 7
+#define GAMEPAD_PS3_BUTTON_L2 4
#define GAMEPAD_PS3_BUTTON_R2 5
-#define GAMEPAD_PS3_BUTTON_L1 6
-#define GAMEPAD_PS3_BUTTON_L2 8
+#define GAMEPAD_PS3_BUTTON_START 8
#define GAMEPAD_PS3_BUTTON_SELECT 9
-#define GAMEPAD_PS3_BUTTON_START 10
-
-// TODO: Add PS3 d-pad axis
+#define GAMEPAD_PS3_BUTTON_UP 24
+#define GAMEPAD_PS3_BUTTON_RIGHT 25
+#define GAMEPAD_PS3_BUTTON_DOWN 26
+#define GAMEPAD_PS3_BUTTON_LEFT 27
+#define GAMEPAD_PS3_BUTTON_PS 12
+
+// PS3 USB Controller Axis
+#define GAMEPAD_PS3_AXIS_LEFT_X 0
+#define GAMEPAD_PS3_AXIS_LEFT_Y 1
+#define GAMEPAD_PS3_AXIS_RIGHT_X 2
+#define GAMEPAD_PS3_AXIS_RIGHT_Y 5
+#define GAMEPAD_PS3_AXIS_L2 3 // 1.0(not pressed) --> -1.0(completely pressed)
+#define GAMEPAD_PS3_AXIS_R2 4 // 1.0(not pressed) --> -1.0(completely pressed)
// Xbox360 USB Controller Buttons
#define GAMEPAD_XBOX_BUTTON_A 0
@@ -218,32 +228,31 @@
#define GAMEPAD_XBOX_BUTTON_RB 5
#define GAMEPAD_XBOX_BUTTON_SELECT 6
#define GAMEPAD_XBOX_BUTTON_START 7
+#define GAMEPAD_XBOX_BUTTON_UP 10
+#define GAMEPAD_XBOX_BUTTON_RIGHT 11
+#define GAMEPAD_XBOX_BUTTON_DOWN 12
+#define GAMEPAD_XBOX_BUTTON_LEFT 13
+#define GAMEPAD_XBOX_BUTTON_HOME 9
+
+// Xbox360 USB Controller Axis
+#define GAMEPAD_XBOX_AXIS_LEFT_X 0
+#define GAMEPAD_XBOX_AXIS_LEFT_Y 1
+#define GAMEPAD_XBOX_AXIS_RIGHT_X 2
+#define GAMEPAD_XBOX_AXIS_RIGHT_Y 3
+#define GAMEPAD_XBOX_AXIS_LT 4 // -1.0(not pressed) --> 1.0(completely pressed)
+#define GAMEPAD_XBOX_AXIS_RT 5 // -1.0(not pressed) --> 1.0(completely pressed)
+/*
+// NOTE: For Raspberry Pi, axis must be reconfigured
#if defined(PLATFORM_RPI)
- #define GAMEPAD_XBOX_AXIS_DPAD_X 7
- #define GAMEPAD_XBOX_AXIS_DPAD_Y 6
+ #define GAMEPAD_XBOX_AXIS_LEFT_X 7
+ #define GAMEPAD_XBOX_AXIS_LEFT_Y 6
#define GAMEPAD_XBOX_AXIS_RIGHT_X 3
#define GAMEPAD_XBOX_AXIS_RIGHT_Y 4
#define GAMEPAD_XBOX_AXIS_LT 2
#define GAMEPAD_XBOX_AXIS_RT 5
-#else
- #define GAMEPAD_XBOX_BUTTON_UP 10
- #define GAMEPAD_XBOX_BUTTON_DOWN 12
- #define GAMEPAD_XBOX_BUTTON_LEFT 13
- #define GAMEPAD_XBOX_BUTTON_RIGHT 11
- #define GAMEPAD_XBOX_AXIS_RIGHT_X 4
- #define GAMEPAD_XBOX_AXIS_RIGHT_Y 3
- #define GAMEPAD_XBOX_AXIS_LT_RT 2
#endif
-
-#define GAMEPAD_XBOX_AXIS_LEFT_X 0
-#define GAMEPAD_XBOX_AXIS_LEFT_Y 1
-
-// Android Physic Buttons
-#define ANDROID_BACK 4
-#define ANDROID_MENU 82
-#define ANDROID_VOLUME_UP 24
-#define ANDROID_VOLUME_DOWN 25
+*/
// NOTE: MSC C++ compiler does not support compound literals (C99 feature)
// Plain structures in C++ (without constructors) can be initialized from { } initializers.
@@ -540,6 +549,21 @@ typedef enum {
COMPRESSED_ASTC_8x8_RGBA // 2 bpp
} TextureFormat;
+// Texture parameters: filter mode
+// NOTE 1: Filtering considers mipmaps if available in the texture
+// NOTE 2: Filter is accordingly set for minification and magnification
+typedef enum {
+ FILTER_POINT = 0, // No filter, just pixel aproximation
+ FILTER_BILINEAR, // Linear filtering
+ FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps)
+ FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x
+ FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x
+ FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x
+} TextureFilterMode;
+
+// Texture parameters: wrap mode
+typedef enum { WRAP_REPEAT = 0, WRAP_CLAMP, WRAP_MIRROR } TextureWrapMode;
+
// Color blending modes (pre-defined)
typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode;
@@ -594,7 +618,7 @@ extern "C" { // Prevents name mangling of functions
// Window and Graphics Device Functions (Module: core)
//------------------------------------------------------------------------------------
#if defined(PLATFORM_ANDROID)
-RLAPI void InitWindow(int width, int height, struct android_app *state); // Init Android Activity and OpenGL Graphics
+RLAPI void InitWindow(int width, int height, void *state); // Init Android Activity and OpenGL Graphics (struct android_app)
#elif defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
RLAPI void InitWindow(int width, int height, const char *title); // Initialize Window and OpenGL Graphics
#endif
@@ -606,11 +630,13 @@ RLAPI void ToggleFullscreen(void); // Fullscreen
RLAPI int GetScreenWidth(void); // Get current screen width
RLAPI int GetScreenHeight(void); // Get current screen height
+#if !defined(PLATFORM_ANDROID)
RLAPI void ShowCursor(void); // Shows cursor
RLAPI void HideCursor(void); // Hides cursor
RLAPI bool IsCursorHidden(void); // Returns true if cursor is not visible
RLAPI void EnableCursor(void); // Enables cursor
RLAPI void DisableCursor(void); // Disables cursor
+#endif
RLAPI void ClearBackground(Color color); // Sets Background Color
RLAPI void BeginDrawing(void); // Setup drawing canvas to start drawing
@@ -653,7 +679,6 @@ RLAPI int StorageLoadValue(int position); // Storage loa
//------------------------------------------------------------------------------------
// Input Handling Functions (Module: core)
//------------------------------------------------------------------------------------
-#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
RLAPI bool IsKeyPressed(int key); // Detect if a key has been pressed once
RLAPI bool IsKeyDown(int key); // Detect if a key is being pressed
RLAPI bool IsKeyReleased(int key); // Detect if a key has been released once
@@ -662,12 +687,15 @@ RLAPI int GetKeyPressed(void); // Get latest key
RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC)
RLAPI bool IsGamepadAvailable(int gamepad); // Detect if a gamepad is available
-RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis
+RLAPI bool IsGamepadName(int gamepad, const char *name); // Check gamepad name (if available)
+RLAPI const char *GetGamepadName(int gamepad); // Return gamepad internal name id
RLAPI bool IsGamepadButtonPressed(int gamepad, int button); // Detect if a gamepad button has been pressed once
RLAPI bool IsGamepadButtonDown(int gamepad, int button); // Detect if a gamepad button is being pressed
RLAPI bool IsGamepadButtonReleased(int gamepad, int button); // Detect if a gamepad button has been released once
RLAPI bool IsGamepadButtonUp(int gamepad, int button); // Detect if a gamepad button is NOT being pressed
-#endif
+RLAPI int GetGamepadButtonPressed(void); // Get the last gamepad button pressed
+RLAPI int GetGamepadAxisCount(int gamepad); // Return gamepad axis count for a gamepad
+RLAPI float GetGamepadAxisMovement(int gamepad, int axis); // Return axis movement value for a gamepad axis
RLAPI bool IsMouseButtonPressed(int button); // Detect if a mouse button has been pressed once
RLAPI bool IsMouseButtonDown(int button); // Detect if a mouse button is being pressed
@@ -683,12 +711,6 @@ RLAPI int GetTouchX(void); // Returns touch p
RLAPI int GetTouchY(void); // Returns touch position Y for touch point 0 (relative to screen size)
RLAPI Vector2 GetTouchPosition(int index); // Returns touch position XY for a touch point index (relative to screen size)
-#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
-
//------------------------------------------------------------------------------------
// Gestures and Touch Handling Functions (Module: gestures)
//------------------------------------------------------------------------------------
@@ -762,6 +784,7 @@ RLAPI void UnloadTexture(Texture2D texture);
RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory
RLAPI Color *GetImageData(Image image); // Get pixel data from image as a Color struct array
RLAPI Image GetTextureData(Texture2D texture); // Get pixel data from GPU texture and return an Image
+RLAPI void UpdateTexture(Texture2D texture, void *pixels); // Update GPU texture with new data
RLAPI void ImageToPOT(Image *image, Color fillColor); // Convert image to POT (power-of-two)
RLAPI void ImageFormat(Image *image, int newFormat); // Convert image data to desired format
RLAPI void ImageAlphaMask(Image *image, Image alphaMask); // Apply alpha mask to image
@@ -783,7 +806,8 @@ RLAPI void ImageColorGrayscale(Image *image);
RLAPI void ImageColorContrast(Image *image, float contrast); // Modify image color: contrast (-100 to 100)
RLAPI void ImageColorBrightness(Image *image, int brightness); // Modify image color: brightness (-255 to 255)
RLAPI void GenTextureMipmaps(Texture2D texture); // Generate GPU mipmaps for a texture
-RLAPI void UpdateTexture(Texture2D texture, void *pixels); // Update GPU texture with new data
+RLAPI void SetTextureFilter(Texture2D texture, int filterMode); // Set texture scaling filter mode
+RLAPI void SetTextureWrap(Texture2D texture, int wrapMode); // Set texture wrapping mode
RLAPI void DrawTexture(Texture2D texture, int posX, int posY, Color tint); // Draw a Texture2D
RLAPI void DrawTextureV(Texture2D texture, Vector2 position, Color tint); // Draw a Texture2D with position defined as Vector2
@@ -797,6 +821,7 @@ RLAPI void DrawTexturePro(Texture2D texture, Rectangle sourceRec, Rectangle dest
//------------------------------------------------------------------------------------
RLAPI SpriteFont GetDefaultFont(void); // Get the default SpriteFont
RLAPI SpriteFont LoadSpriteFont(const char *fileName); // Load a SpriteFont image into GPU memory
+RLAPI SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars); // Load a SpriteFont from TTF font with parameters
RLAPI void UnloadSpriteFont(SpriteFont spriteFont); // Unload SpriteFont from GPU memory
RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color color); // Draw text (using default font)
@@ -896,7 +921,8 @@ RLAPI void DestroyLight(Light light); // Des
RLAPI void InitVrDevice(int vdDevice); // Init VR device
RLAPI void CloseVrDevice(void); // Close VR device
RLAPI bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready
-RLAPI void UpdateVrTracking(void); // Update VR tracking (position and orientation)
+RLAPI bool IsVrSimulator(void); // Detect if VR simulator is running
+RLAPI void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera
RLAPI void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator)
//------------------------------------------------------------------------------------
@@ -912,7 +938,7 @@ RLAPI Sound LoadSound(const char *fileName); // Load so
RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound to memory from wave data
RLAPI Sound LoadSoundFromRES(const char *rresName, int resId); // Load sound to memory from rRES file (raylib Resource)
RLAPI void UpdateSound(Sound sound, void *data, int numSamples); // Update sound buffer with new data
-RLAPI void UnloadWave(Wave wave);
+RLAPI void UnloadWave(Wave wave); // Unload wave data
RLAPI void UnloadSound(Sound sound); // Unload sound
RLAPI void PlaySound(Sound sound); // Play a sound
RLAPI void PauseSound(Sound sound); // Pause a sound
diff --git a/src/rlgl.c b/src/rlgl.c
index 244de52c..e2804e9c 100644
--- a/src/rlgl.c
+++ b/src/rlgl.c
@@ -35,7 +35,7 @@
#include <math.h> // Required for: atan2()
#ifndef RLGL_STANDALONE
- #include "raymath.h" // Required for Vector3 and Matrix functions
+ #include "raymath.h" // Required for: Vector3 and Matrix functions
#endif
#if defined(GRAPHICS_API_OPENGL_11)
@@ -140,6 +140,14 @@
#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93b7
#endif
+#ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
+ #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF
+#endif
+
+#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT
+ #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE
+#endif
+
#if defined(GRAPHICS_API_OPENGL_11)
#define GL_UNSIGNED_SHORT_5_6_5 0x8363
#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034
@@ -283,14 +291,21 @@ static Shader standardShader; // Shader with support for lighting
static Shader currentShader; // Shader to be used on rendering (by default, defaultShader)
static bool standardShaderLoaded = false; // Flag to track if standard shader has been loaded
-// Flags for supported extensions
+// Extension supported flag: VAO
static bool vaoSupported = false; // VAO support (OpenGL ES2 could not support VAO extension)
-// Compressed textures support flags
+// Extension supported flag: Compressed textures
static bool texCompETC1Supported = false; // ETC1 texture compression support
static bool texCompETC2Supported = false; // ETC2/EAC texture compression support
static bool texCompPVRTSupported = false; // PVR texture compression support
static bool texCompASTCSupported = false; // ASTC texture compression support
+
+// Extension supported flag: Anisotropic filtering
+static bool texAnisotropicFilterSupported = false; // Anisotropic texture filtering support
+static float maxAnisotropicLevel = 0.0f; // Maximum anisotropy level supported (minimum is 2.0f)
+
+// Extension supported flag: Clamp mirror wrap mode
+static bool texClampMirrorSupported = false; // Clamp mirror wrap mode supported
#endif
#if defined(RLGL_OCULUS_SUPPORT)
@@ -372,11 +387,11 @@ static char *ReadTextFile(const char *fileName); // Read chars array from
#if defined(RLGL_OCULUS_SUPPORT)
#if !defined(RLGL_STANDALONE)
-static bool InitOculusDevice(void); // Initialize Oculus device (returns true if success)
-static void CloseOculusDevice(void); // Close Oculus device
-static void UpdateOculusTracking(void); // Update Oculus head position-orientation tracking
-static void BeginOculusDrawing(void); // Setup Oculus buffers for drawing
-static void EndOculusDrawing(void); // Finish Oculus drawing and blit framebuffer to mirror
+static bool InitOculusDevice(void); // Initialize Oculus device (returns true if success)
+static void CloseOculusDevice(void); // Close Oculus device
+static void UpdateOculusTracking(Camera *camera); // Update Oculus head position-orientation tracking
+static void BeginOculusDrawing(void); // Setup Oculus buffers for drawing
+static void EndOculusDrawing(void); // Finish Oculus drawing and blit framebuffer to mirror
#endif
static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height); // Load Oculus required buffers
@@ -871,6 +886,37 @@ void rlDisableTexture(void)
#endif
}
+// Set texture parameters (wrap mode/filter mode)
+void rlTextureParameters(unsigned int id, int param, int value)
+{
+ glBindTexture(GL_TEXTURE_2D, id);
+
+ switch (param)
+ {
+ case RL_TEXTURE_WRAP_S:
+ case RL_TEXTURE_WRAP_T:
+ {
+ if ((value == RL_WRAP_CLAMP_MIRROR) && !texClampMirrorSupported) TraceLog(WARNING, "Clamp mirror wrap mode not supported");
+ else glTexParameteri(GL_TEXTURE_2D, param, value);
+ } break;
+ case RL_TEXTURE_MAG_FILTER:
+ case RL_TEXTURE_MIN_FILTER: glTexParameteri(GL_TEXTURE_2D, param, value); break;
+ case RL_TEXTURE_ANISOTROPIC_FILTER:
+ {
+ if (value <= maxAnisotropicLevel) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, value);
+ else if (maxAnisotropicLevel > 0.0f)
+ {
+ TraceLog(WARNING, "[TEX ID %i] Maximum anisotropic filter level supported is %iX", id, maxAnisotropicLevel);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, value);
+ }
+ else TraceLog(WARNING, "Anisotropic filtering not supported");
+ } break;
+ default: break;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+}
+
// Enable rendering to texture (fbo)
void rlEnableRenderTexture(unsigned int id)
{
@@ -1124,7 +1170,7 @@ void rlglInit(int width, int height)
// Check NPOT textures support
// NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature
if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) npotSupported = true;
-#endif
+#endif
// DDS texture compression support
if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) ||
@@ -1143,6 +1189,16 @@ void rlglInit(int width, int height)
// ASTC texture compression support
if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true;
+
+ // Anisotropic texture filter support
+ if (strcmp(extList[i], (const char *)"GL_EXT_texture_filter_anisotropic") == 0)
+ {
+ texAnisotropicFilterSupported = true;
+ glGetFloatv(0x84FF, &maxAnisotropicLevel); // GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT
+ }
+
+ // Clamp mirror wrap mode supported
+ if (strcmp(extList[i], (const char *)"GL_EXT_texture_mirror_clamp") == 0) texClampMirrorSupported = true;
}
#ifdef _MSC_VER
@@ -1162,6 +1218,9 @@ void rlglInit(int width, int height)
if (texCompETC2Supported) TraceLog(INFO, "[EXTENSION] ETC2/EAC compressed textures supported");
if (texCompPVRTSupported) TraceLog(INFO, "[EXTENSION] PVRT compressed textures supported");
if (texCompASTCSupported) TraceLog(INFO, "[EXTENSION] ASTC compressed textures supported");
+
+ if (texAnisotropicFilterSupported) TraceLog(INFO, "[EXTENSION] Anisotropic textures filtering supported (max: %.0fX)", maxAnisotropicLevel);
+ if (texClampMirrorSupported) TraceLog(INFO, "[EXTENSION] Clamp mirror wrap texture mode supported");
// Initialize buffers, default shaders and default textures
//----------------------------------------------------------
@@ -1522,7 +1581,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height)
target.texture.id = 0;
target.texture.width = width;
target.texture.height = height;
- target.texture.format = UNCOMPRESSED_R8G8B8;
+ target.texture.format = UNCOMPRESSED_R8G8B8A8;
target.texture.mipmaps = 1;
target.depth.id = 0;
@@ -1539,7 +1598,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
#if defined(GRAPHICS_API_OPENGL_33)
@@ -1687,6 +1746,7 @@ void rlglGenerateMipmaps(Texture2D texture)
#endif
#if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
+ //glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE); // Hint for mipmaps generation algorythm: GL_FASTEST, GL_NICEST, GL_DONT_CARE
glGenerateMipmap(GL_TEXTURE_2D); // Generate mipmaps automatically
TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture.id);
@@ -2653,7 +2713,7 @@ void InitVrDevice(int vrDevice)
hmd.hResolution = 2160; // HMD horizontal resolution in pixels
hmd.vResolution = 1200; // HMD vertical resolution in pixels
hmd.hScreenSize = 0.133793f; // HMD horizontal size in meters
- hmd.vScreenSize = 0.0669; // HMD vertical size in meters
+ hmd.vScreenSize = 0.0669f; // HMD vertical size in meters
hmd.vScreenCenter = 0.04678f; // HMD screen center in meters
hmd.eyeToScreenDistance = 0.041f; // HMD distance between eye and display in meters
hmd.lensSeparationDistance = 0.07f; // HMD lens separation distance in meters
@@ -2710,6 +2770,12 @@ bool IsVrDeviceReady(void)
return (vrDeviceReady || vrSimulator) && vrEnabled;
}
+// Detect if VR simulator is running
+bool IsVrSimulator(void)
+{
+ return vrSimulator;
+}
+
// Enable/Disable VR experience (device or simulator)
void ToggleVrMode(void)
{
@@ -2728,16 +2794,13 @@ void ToggleVrMode(void)
#endif
}
-// Update VR tracking (position and orientation)
-void UpdateVrTracking(void)
+// Update VR tracking (position and orientation) and camera
+// NOTE: Camera (position, target, up) gets update with head tracking information
+void UpdateVrTracking(Camera *camera)
{
#if defined(RLGL_OCULUS_SUPPORT)
- if (vrDeviceReady) UpdateOculusTracking();
- else
+ if (vrDeviceReady) UpdateOculusTracking(camera);
#endif
- {
- // TODO: Use alternative inputs (mouse, keyboard) to simulate tracking data (eyes position/orientation)
- }
}
// Begin Oculus drawing configuration
@@ -3779,8 +3842,8 @@ static void SetStereoConfig(VrDeviceInfo hmd)
// Compute lens parameters
float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize;
- float leftLensCenter[2] = { 0.25 + lensShift, 0.5f };
- float rightLensCenter[2] = { 0.75 - lensShift, 0.5f };
+ float leftLensCenter[2] = { 0.25f + lensShift, 0.5f };
+ float rightLensCenter[2] = { 0.75f - lensShift, 0.5f };
float leftScreenCenter[2] = { 0.25f, 0.5f };
float rightScreenCenter[2] = { 0.75f, 0.5f };
@@ -3797,8 +3860,8 @@ static void SetStereoConfig(VrDeviceInfo hmd)
float normScreenWidth = 0.5f;
float normScreenHeight = 1.0f;
- float scaleIn[2] = { 2/normScreenWidth, 2/normScreenHeight/aspect };
- float scale[2] = { normScreenWidth*0.5/distortionScale, normScreenHeight*0.5*aspect/distortionScale };
+ float scaleIn[2] = { 2.0f/normScreenWidth, 2.0f/normScreenHeight/aspect };
+ float scale[2] = { normScreenWidth*0.5f/distortionScale, normScreenHeight*0.5f*aspect/distortionScale };
TraceLog(DEBUG, "VR: Distortion Shader: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]);
TraceLog(DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[1]);
@@ -4076,7 +4139,7 @@ OCULUSAPI void CloseOculusDevice(void)
}
// Update Oculus head position-orientation tracking
-OCULUSAPI void UpdateOculusTracking(void)
+OCULUSAPI void UpdateOculusTracking(Camera *camera)
{
frameIndex++;
@@ -4086,6 +4149,10 @@ OCULUSAPI void UpdateOculusTracking(void)
layer.eyeLayer.RenderPose[0] = eyePoses[0];
layer.eyeLayer.RenderPose[1] = eyePoses[1];
+ // TODO: Update external camera with eyePoses data (position, orientation)
+ // NOTE: We can simplify to simple camera if we consider IPD and HMD device configuration again later
+ // it will be useful for the user to draw, lets say, billboards oriented to camera
+
// Get session status information
ovrSessionStatus sessionStatus;
ovr_GetSessionStatus(session, &sessionStatus);
diff --git a/src/rlgl.h b/src/rlgl.h
index 5fc9f8b9..9be73f36 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -90,15 +90,33 @@
#define MAX_QUADS_BATCH 1024 // Be careful with text, every letter maps a quad
#endif
+// Texture parameters (equivalent to OpenGL defines)
+#define RL_TEXTURE_WRAP_S 0x2802 // GL_TEXTURE_WRAP_S
+#define RL_TEXTURE_WRAP_T 0x2803 // GL_TEXTURE_WRAP_T
+#define RL_TEXTURE_MAG_FILTER 0x2800 // GL_TEXTURE_MAG_FILTER
+#define RL_TEXTURE_MIN_FILTER 0x2801 // GL_TEXTURE_MIN_FILTER
+#define RL_TEXTURE_ANISOTROPIC_FILTER 0x3000 // Anisotropic filter (custom identifier)
+
+#define RL_FILTER_NEAREST 0x2600 // GL_NEAREST
+#define RL_FILTER_LINEAR 0x2601 // GL_LINEAR
+#define RL_FILTER_MIP_NEAREST 0x2700 // GL_NEAREST_MIPMAP_NEAREST
+#define RL_FILTER_NEAREST_MIP_LINEAR 0x2702 // GL_NEAREST_MIPMAP_LINEAR
+#define RL_FILTER_LINEAR_MIP_NEAREST 0x2701 // GL_LINEAR_MIPMAP_NEAREST
+#define RL_FILTER_MIP_LINEAR 0x2703 // GL_LINEAR_MIPMAP_LINEAR
+
+#define RL_WRAP_REPEAT 0x2901 // GL_REPEAT
+#define RL_WRAP_CLAMP 0x812F // GL_CLAMP_TO_EDGE
+#define RL_WRAP_CLAMP_MIRROR 0x8742 // GL_MIRROR_CLAMP_EXT
+
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
+typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
+
typedef enum { RL_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode;
typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode;
-typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
-
#if defined(RLGL_STANDALONE)
#ifndef __cplusplus
// Boolean type
@@ -236,6 +254,21 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
// Light types
typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType;
+
+ // Texture parameters: filter mode
+ // NOTE 1: Filtering considers mipmaps if available in the texture
+ // NOTE 2: Filter is accordingly set for minification and magnification
+ typedef enum {
+ FILTER_POINT = 0, // No filter, just pixel aproximation
+ FILTER_BILINEAR, // Linear filtering
+ FILTER_TRILINEAR, // Trilinear filtering (linear with mipmaps)
+ FILTER_ANISOTROPIC_4X, // Anisotropic filtering 4x
+ FILTER_ANISOTROPIC_8X, // Anisotropic filtering 8x
+ FILTER_ANISOTROPIC_16X, // Anisotropic filtering 16x
+ } TextureFilterMode;
+
+ // Texture parameters: wrap mode
+ typedef enum { WRAP_REPEAT = 0, WRAP_CLAMP, WRAP_MIRROR } TextureWrapMode;
// Color blending modes (pre-defined)
typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode;
@@ -296,6 +329,7 @@ void rlColor4f(float x, float y, float z, float w); // Define one vertex (color)
//------------------------------------------------------------------------------------
void rlEnableTexture(unsigned int id); // Enable texture usage
void rlDisableTexture(void); // Disable texture usage
+void rlTextureParameters(unsigned int id, int param, int value); // Set texture parameters (filter, wrap)
void rlEnableRenderTexture(unsigned int id); // Enable render texture (fbo)
void rlDisableRenderTexture(void); // Disable render texture (fbo), return to default framebuffer
void rlEnableDepthTest(void); // Enable depth test
@@ -375,13 +409,13 @@ float *MatrixToFloat(Matrix mat);
void InitVrDevice(int vrDevice); // Init VR device
void CloseVrDevice(void); // Close VR device
bool IsVrDeviceReady(void); // Detect if VR device (or simulator) is ready
-void UpdateVrTracking(void); // Update VR tracking (position and orientation)
+void UpdateVrTracking(Camera *camera); // Update VR tracking (position and orientation) and camera
void ToggleVrMode(void); // Enable/Disable VR experience (device or simulator)
// Oculus Rift API for direct access the device (no simulator)
bool InitOculusDevice(void); // Initialize Oculus device (returns true if success)
void CloseOculusDevice(void); // Close Oculus device
-void UpdateOculusTracking(void); // Update Oculus head position-orientation tracking
+void UpdateOculusTracking(Camera *camera); // Update Oculus head position-orientation tracking (and camera)
void BeginOculusDrawing(void); // Setup Oculus buffers for drawing
void EndOculusDrawing(void); // Finish Oculus drawing and blit framebuffer to mirror
#endif
diff --git a/src/rlua.h b/src/rlua.h
index 1c8c7b38..97d22922 100644
--- a/src/rlua.h
+++ b/src/rlua.h
@@ -2824,6 +2824,28 @@ int lua_IsAudioDeviceReady(lua_State* L)
return 1;
}
+int lua_LoadWave(lua_State* L)
+{
+ const char * arg1 = LuaGetArgument_string(L, 1);
+ Wave result = LoadWave((char *)arg1);
+ LuaPush_Wave(L, result);
+ return 1;
+}
+
+int lua_LoadWaveEx(lua_State* L)
+{
+ // TODO: Wave LoadWaveEx(float *data, int sampleCount, int sampleRate, int sampleSize, int channels);
+
+ int arg1 = 0;
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ int arg5 = LuaGetArgument_int(L, 5);
+ Wave result = LoadWaveEx(arg1, arg2, arg3, arg4, arg5);
+ LuaPush_Wave(L, result);
+ return 1;
+}
+
int lua_LoadSound(lua_State* L)
{
const char * arg1 = LuaGetArgument_string(L, 1);
@@ -2849,6 +2871,22 @@ int lua_LoadSoundFromRES(lua_State* L)
return 1;
}
+int lua_UpdateSound(lua_State* L)
+{
+ Sound arg1 = LuaGetArgument_Sound(L, 1);
+ const char * arg2 = LuaGetArgument_string(L, 2);
+ int * arg3 = LuaGetArgument_int(L, 3);
+ UpdateSound(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_UnloadWave(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ UnloadWave(arg1);
+ return 0;
+}
+
int lua_UnloadSound(lua_State* L)
{
Sound arg1 = LuaGetArgument_Sound(L, 1);
@@ -2908,6 +2946,43 @@ int lua_SetSoundPitch(lua_State* L)
return 0;
}
+int lua_WaveFormat(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ int arg4 = LuaGetArgument_int(L, 4);
+ WaveFormat(arg1, arg2, arg3, arg4);
+ return 0;
+}
+
+int lua_LoadMusicStream(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ Wave result = WaveCopy(arg1);
+ LuaPush_Wave(L, result);
+ return 1;
+}
+
+int lua_WaveCrop(lua_State* L)
+{
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ int arg2 = LuaGetArgument_int(L, 2);
+ int arg3 = LuaGetArgument_int(L, 3);
+ WaveCrop(arg1, arg2, arg3);
+ return 0;
+}
+
+int lua_GetWaveData(lua_State* L)
+{
+ // TODO: float *GetWaveData(Wave wave);
+
+ Wave arg1 = LuaGetArgument_Wave(L, 1);
+ float result = GetWaveData(arg1);
+ LuaPush_float(L, result);
+ return 1;
+}
+
int lua_LoadMusicStream(lua_State* L)
{
const char * arg1 = LuaGetArgument_string(L, 1);
@@ -3799,9 +3874,13 @@ static luaL_Reg raylib_functions[] = {
REG(InitAudioDevice)
REG(CloseAudioDevice)
REG(IsAudioDeviceReady)
+ REG(LoadWave)
+ REG(LoadWaveEx)
REG(LoadSound)
REG(LoadSoundFromWave)
REG(LoadSoundFromRES)
+ REG(UpdateSound)
+ REG(UnloadWave)
REG(UnloadSound)
REG(PlaySound)
REG(PauseSound)
@@ -3810,6 +3889,10 @@ static luaL_Reg raylib_functions[] = {
REG(IsSoundPlaying)
REG(SetSoundVolume)
REG(SetSoundPitch)
+ REG(WaveFormat)
+ REG(WaveCopy)
+ REG(WaveCrop)
+ REG(GetWaveData)
REG(LoadMusicStream)
REG(UnloadMusicStream)
diff --git a/src/shader_standard.h b/src/shader_standard.h
index deae7fe1..995c62ea 100644
--- a/src/shader_standard.h
+++ b/src/shader_standard.h
@@ -90,7 +90,7 @@ static const char fStandardShaderStr[] =
" if (diff > 0.0)\n"
" {\n"
" vec3 h = normalize(-l.direction + v);\n"
-" spec = pow(dot(n, h), 3.0 + glossiness)*s;\n"
+" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n"
" }\n"
" return (diff*l.diffuse.rgb + spec*colSpecular.rgb);\n"
"}\n"
@@ -103,7 +103,7 @@ static const char fStandardShaderStr[] =
" if (diff > 0.0)\n"
" {\n"
" vec3 h = normalize(lightDir + v);\n"
-" spec = pow(dot(n, h), 3.0 + glossiness)*s;\n"
+" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n"
" }\n"
" return (diff*l.intensity*l.diffuse.rgb + spec*colSpecular.rgb);\n"
"}\n"
@@ -124,7 +124,7 @@ static const char fStandardShaderStr[] =
" if (diffAttenuation > 0.0)\n"
" {\n"
" vec3 h = normalize(lightDir + v);\n"
-" spec = pow(dot(n, h), 3.0 + glossiness)*s;\n"
+" spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;\n"
" }\n"
" return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));\n"
"}\n"
@@ -152,9 +152,9 @@ static const char fStandardShaderStr[] =
" }\n"
" float spec = 1.0;\n"
#if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21)
-" if (useSpecular == 1) spec *= normalize(texture2D(texture2, fragTexCoord).r);\n"
+" if (useSpecular == 1) spec = texture2D(texture2, fragTexCoord).r;\n"
#elif defined(GRAPHICS_API_OPENGL_33)
-" if (useSpecular == 1) spec *= normalize(texture(texture2, fragTexCoord).r);\n"
+" if (useSpecular == 1) spec = texture(texture2, fragTexCoord).r;\n"
#endif
" for (int i = 0; i < maxLights; i++)\n"
" {\n"
diff --git a/src/shapes.c b/src/shapes.c
index 362dc0f7..79cf567a 100644
--- a/src/shapes.c
+++ b/src/shapes.c
@@ -4,7 +4,7 @@
*
* Basic functions to draw 2d Shapes and check collisions
*
-* Copyright (c) 2014 Ramon Santamaria (@raysan5)
+* Copyright (c) 2014-2016 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.
@@ -25,9 +25,8 @@
#include "raylib.h"
-#include <stdlib.h> // Required for abs() function
-#include <math.h> // Math related functions, sin() and cos() used on DrawCircle*
- // sqrt() and pow() and abs() used on CheckCollision*
+#include <stdlib.h> // Required for: abs()
+#include <math.h> // Required for: sinf(), cosf(), sqrtf()
#include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2
@@ -71,7 +70,7 @@ void DrawPixelV(Vector2 position, Color color)
rlBegin(RL_LINES);
rlColor4ub(color.r, color.g, color.b, color.a);
rlVertex2f(position.x, position.y);
- rlVertex2i(position.x + 1, position.y + 1);
+ rlVertex2f(position.x + 1.0f, position.y + 1.0f);
rlEnd();
}
@@ -98,7 +97,7 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
// Draw a color-filled circle
void DrawCircle(int centerX, int centerY, float radius, Color color)
{
- DrawCircleV((Vector2){ centerX, centerY }, radius, color);
+ DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color);
}
// Draw a gradient-filled circle
@@ -111,9 +110,9 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co
rlColor4ub(color1.r, color1.g, color1.b, color1.a);
rlVertex2i(centerX, centerY);
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
- rlVertex2f(centerX + sin(DEG2RAD*i)*radius, centerY + cos(DEG2RAD*i)*radius);
+ rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius);
rlColor4ub(color2.r, color2.g, color2.b, color2.a);
- rlVertex2f(centerX + sin(DEG2RAD*(i + 10))*radius, centerY + cos(DEG2RAD*(i + 10))*radius);
+ rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius);
}
rlEnd();
}
@@ -129,9 +128,9 @@ void DrawCircleV(Vector2 center, float radius, Color color)
{
rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(center.x, center.y);
- rlVertex2f(center.x + sin(DEG2RAD*i)*radius, center.y + cos(DEG2RAD*i)*radius);
- rlVertex2f(center.x + sin(DEG2RAD*(i + 10))*radius, center.y + cos(DEG2RAD*(i + 10))*radius);
+ rlVertex2f(center.x, center.y);
+ rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
+ rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
}
rlEnd();
}
@@ -144,10 +143,10 @@ void DrawCircleV(Vector2 center, float radius, Color color)
{
rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(center.x, center.y);
- rlVertex2f(center.x + sin(DEG2RAD*i)*radius, center.y + cos(DEG2RAD*i)*radius);
- rlVertex2f(center.x + sin(DEG2RAD*(i + 10))*radius, center.y + cos(DEG2RAD*(i + 10))*radius);
- rlVertex2f(center.x + sin(DEG2RAD*(i + 20))*radius, center.y + cos(DEG2RAD*(i + 20))*radius);
+ rlVertex2f(center.x, center.y);
+ rlVertex2f(center.x + sinf(DEG2RAD*i)*radius, center.y + cosf(DEG2RAD*i)*radius);
+ rlVertex2f(center.x + sinf(DEG2RAD*(i + 10))*radius, center.y + cosf(DEG2RAD*(i + 10))*radius);
+ rlVertex2f(center.x + sinf(DEG2RAD*(i + 20))*radius, center.y + cosf(DEG2RAD*(i + 20))*radius);
}
rlEnd();
@@ -164,8 +163,8 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color)
// NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360)
for (int i = 0; i < 360; i += 10)
{
- rlVertex2f(centerX + sin(DEG2RAD*i)*radius, centerY + cos(DEG2RAD*i)*radius);
- rlVertex2f(centerX + sin(DEG2RAD*(i + 10))*radius, centerY + cos(DEG2RAD*(i + 10))*radius);
+ rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius);
+ rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius);
}
rlEnd();
}
@@ -330,9 +329,9 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col
{
rlColor4ub(color.r, color.g, color.b, color.a);
- rlVertex2i(0, 0);
- rlVertex2f(sin(DEG2RAD*i)*radius, cos(DEG2RAD*i)*radius);
- rlVertex2f(sin(DEG2RAD*(i + 360/sides))*radius, cos(DEG2RAD*(i + 360/sides))*radius);
+ rlVertex2f(0, 0);
+ rlVertex2f(sinf(DEG2RAD*i)*radius, cosf(DEG2RAD*i)*radius);
+ rlVertex2f(sinf(DEG2RAD*(i + 360/sides))*radius, cosf(DEG2RAD*(i + 360/sides))*radius);
}
rlEnd();
rlPopMatrix();
@@ -434,7 +433,7 @@ bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, floa
float dx = center2.x - center1.x; // X distance between centers
float dy = center2.y - center1.y; // Y distance between centers
- float distance = sqrt(dx*dx + dy*dy); // Distance between centers
+ float distance = sqrtf(dx*dx + dy*dy); // Distance between centers
if (distance <= (radius1 + radius2)) collision = true;
@@ -457,7 +456,7 @@ bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
if (dx <= (rec.width/2)) { return true; }
if (dy <= (rec.height/2)) { return true; }
- float cornerDistanceSq = pow(dx - rec.width/2, 2) + pow(dy - rec.height/2, 2);
+ float cornerDistanceSq = (dx - rec.width/2)*(dx - rec.width/2) + (dy - rec.height/2)*(dy - rec.height/2);
return (cornerDistanceSq <= (radius*radius));
}
diff --git a/src/text.c b/src/text.c
index c538ea56..23041603 100644
--- a/src/text.c
+++ b/src/text.c
@@ -4,7 +4,7 @@
*
* Basic functions to load SpriteFonts and draw Text
*
-* Copyright (c) 2014 Ramon Santamaria (@raysan5)
+* Copyright (c) 2014-2016 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.
@@ -30,9 +30,10 @@
#include <stdarg.h> // Required for: va_list, va_start(), vfprintf(), va_end()
#include <stdio.h> // Required for: FILE, fopen(), fclose(), fscanf(), feof(), rewind(), fgets()
-#include "utils.h" // Required for: GetExtension()
+#include "utils.h" // Required for: GetExtension(), GetNextPOT()
// Following libs are used on LoadTTF()
+//#define STBTT_STATIC
#define STB_TRUETYPE_IMPLEMENTATION
#include "external/stb_truetype.h" // Required for: stbtt_BakeFontBitmap()
@@ -43,7 +44,6 @@
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
-#define FONT_FIRST_CHAR 32 // NOTE: Expected first char for a sprite font
#define MAX_FORMATTEXT_LENGTH 64
#define MAX_SUBTEXT_LENGTH 64
@@ -68,12 +68,12 @@ static SpriteFont defaultFont; // Default font provided by raylib
//----------------------------------------------------------------------------------
// Module specific Functions Declaration
//----------------------------------------------------------------------------------
+static int GetCharIndex(SpriteFont font, int letter);
+
static SpriteFont LoadImageFont(Image image, Color key, int firstChar); // Load a Image font file (XNA style)
static SpriteFont LoadRBMF(const char *fileName); // Load a rBMF font file (raylib BitMap Font)
static SpriteFont LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file)
-
-// Generate a sprite font image from TTF data
-static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars);
+static SpriteFont LoadTTF(const char *fileName, int fontSize, int numChars, int *fontChars); // Load spritefont from TTF data
extern void LoadDefaultFont(void);
extern void UnloadDefaultFont(void);
@@ -198,7 +198,7 @@ extern void LoadDefaultFont(void)
for (int i = 0; i < defaultFont.numChars; i++)
{
- defaultFont.charValues[i] = FONT_FIRST_CHAR + i; // First char is 32
+ defaultFont.charValues[i] = 32 + i; // First char is 32
defaultFont.charRecs[i].x = currentPosX;
defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor);
@@ -249,17 +249,18 @@ SpriteFont LoadSpriteFont(const char *fileName)
// Default hardcoded values for ttf file loading
#define DEFAULT_TTF_FONTSIZE 32 // Font first character (32 - space)
#define DEFAULT_TTF_NUMCHARS 95 // ASCII 32..126 is 95 glyphs
+ #define DEFAULT_FIRST_CHAR 32 // Expected first char for image spritefont
SpriteFont spriteFont = { 0 };
// Check file extension
if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName);
- else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadTTF(fileName, DEFAULT_TTF_FONTSIZE, FONT_FIRST_CHAR, DEFAULT_TTF_NUMCHARS);
+ else if (strcmp(GetExtension(fileName),"ttf") == 0) spriteFont = LoadSpriteFontTTF(fileName, DEFAULT_TTF_FONTSIZE, 0, NULL);
else if (strcmp(GetExtension(fileName),"fnt") == 0) spriteFont = LoadBMFont(fileName);
else
{
Image image = LoadImage(fileName);
- if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, FONT_FIRST_CHAR);
+ if (image.data != NULL) spriteFont = LoadImageFont(image, MAGENTA, DEFAULT_FIRST_CHAR);
UnloadImage(image);
}
@@ -268,6 +269,38 @@ SpriteFont LoadSpriteFont(const char *fileName)
TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName);
spriteFont = GetDefaultFont();
}
+ else SetTextureFilter(spriteFont.texture, FILTER_POINT); // By default we set point filter (best performance)
+
+ return spriteFont;
+}
+
+// Load SpriteFont from TTF file with custom parameters
+// NOTE: You can pass an array with desired characters, those characters should be available in the font
+// if array is NULL, default char set is selected 32..126
+SpriteFont LoadSpriteFontTTF(const char *fileName, int fontSize, int numChars, int *fontChars)
+{
+ SpriteFont spriteFont = { 0 };
+
+ if (strcmp(GetExtension(fileName),"ttf") == 0)
+ {
+ if ((fontChars == NULL) || (numChars == 0))
+ {
+ int totalChars = 95; // Default charset [32..126]
+
+ int *defaultFontChars = (int *)malloc(totalChars*sizeof(int));
+
+ for (int i = 0; i < totalChars; i++) defaultFontChars[i] = i + 32; // Default first character: SPACE[32]
+
+ spriteFont = LoadTTF(fileName, fontSize, totalChars, defaultFontChars);
+ }
+ else spriteFont = LoadTTF(fileName, fontSize, numChars, fontChars);
+ }
+
+ if (spriteFont.texture.id == 0)
+ {
+ TraceLog(WARNING, "[%s] SpriteFont could not be generated, using default font", fileName);
+ spriteFont = GetDefaultFont();
+ }
return spriteFont;
}
@@ -293,13 +326,17 @@ void UnloadSpriteFont(SpriteFont spriteFont)
// NOTE: chars spacing is proportional to fontSize
void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
{
- Vector2 position = { (float)posX, (float)posY };
+ // Check if default font has been loaded
+ if (defaultFont.texture.id != 0)
+ {
+ Vector2 position = { (float)posX, (float)posY };
- int defaultFontSize = 10; // Default Font chars height in pixel
- if (fontSize < defaultFontSize) fontSize = defaultFontSize;
- int spacing = fontSize/defaultFontSize;
+ int defaultFontSize = 10; // Default Font chars height in pixel
+ if (fontSize < defaultFontSize) fontSize = defaultFontSize;
+ int spacing = fontSize/defaultFontSize;
- DrawTextEx(defaultFont, text, position, (float)fontSize, spacing, color);
+ DrawTextEx(GetDefaultFont(), text, position, (float)fontSize, spacing, color);
+ }
}
// Draw text using SpriteFont
@@ -321,22 +358,18 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float
for (int i = 0; i < length; i++)
{
- // TODO: Right now we are supposing characters that follow a continous order and start at FONT_FIRST_CHAR,
- // this sytem can be improved to support any characters order and init value...
- // An intermediate table could be created to link char values with predefined char position index in chars rectangle array
-
if ((unsigned char)text[i] == 0xc2) // UTF-8 encoding identification HACK!
{
// Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿)
letter = (unsigned char)text[i + 1];
- rec = spriteFont.charRecs[letter - FONT_FIRST_CHAR];
+ rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)letter)];
i++;
}
else if ((unsigned char)text[i] == 0xc3) // UTF-8 encoding identification HACK!
{
// Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ)
letter = (unsigned char)text[i + 1];
- rec = spriteFont.charRecs[letter - FONT_FIRST_CHAR + 64];
+ rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)letter + 64)];
i++;
}
else
@@ -348,17 +381,19 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float
textOffsetX = 0;
rec.x = -1;
}
- else rec = spriteFont.charRecs[(int)text[i] - FONT_FIRST_CHAR];
+ else rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)text[i])];
}
- if (rec.x > 0)
+ if (rec.x >= 0)
{
- DrawTexturePro(spriteFont.texture, rec, (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].x*scaleFactor,
- position.y + textOffsetY + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].y*scaleFactor,
+ int index = GetCharIndex(spriteFont, (int)text[i]);
+
+ DrawTexturePro(spriteFont.texture, rec, (Rectangle){ position.x + textOffsetX + spriteFont.charOffsets[index].x*scaleFactor,
+ position.y + textOffsetY + spriteFont.charOffsets[index].y*scaleFactor,
rec.width*scaleFactor, rec.height*scaleFactor} , (Vector2){ 0, 0 }, 0.0f, tint);
- if (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR] == 0) textOffsetX += (rec.width*scaleFactor + spacing);
- else textOffsetX += (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR]*scaleFactor + spacing);
+ if (spriteFont.charAdvanceX[index] == 0) textOffsetX += (rec.width*scaleFactor + spacing);
+ else textOffsetX += (spriteFont.charAdvanceX[index]*scaleFactor + spacing);
}
}
}
@@ -404,13 +439,17 @@ const char *SubText(const char *text, int position, int length)
// Measure string width for default font
int MeasureText(const char *text, int fontSize)
{
- Vector2 vec;
+ Vector2 vec = { 0.0f, 0.0f };
- int defaultFontSize = 10; // Default Font chars height in pixel
- if (fontSize < defaultFontSize) fontSize = defaultFontSize;
- int spacing = fontSize/defaultFontSize;
+ // Check if default font has been loaded
+ if (defaultFont.texture.id != 0)
+ {
+ int defaultFontSize = 10; // Default Font chars height in pixel
+ if (fontSize < defaultFontSize) fontSize = defaultFontSize;
+ int spacing = fontSize/defaultFontSize;
- vec = MeasureTextEx(defaultFont, text, fontSize, spacing);
+ vec = MeasureTextEx(GetDefaultFont(), text, fontSize, spacing);
+ }
return (int)vec.x;
}
@@ -434,8 +473,10 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int
if (text[i] != '\n')
{
- if (spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR] != 0) textWidth += spriteFont.charAdvanceX[(int)text[i] - FONT_FIRST_CHAR];
- else textWidth += (spriteFont.charRecs[(int)text[i] - FONT_FIRST_CHAR].width + spriteFont.charOffsets[(int)text[i] - FONT_FIRST_CHAR].x);
+ int index = GetCharIndex(spriteFont, (int)text[i]);
+
+ if (spriteFont.charAdvanceX[index] != 0) textWidth += spriteFont.charAdvanceX[index];
+ else textWidth += (spriteFont.charRecs[index].width + spriteFont.charOffsets[index].x);
}
else
{
@@ -492,7 +533,28 @@ void DrawFPS(int posX, int posY)
// Module specific Functions Definition
//----------------------------------------------------------------------------------
-// Load a Image font file (XNA style)
+static int GetCharIndex(SpriteFont font, int letter)
+{
+//#define UNORDERED_CHARSET
+#if defined(UNORDERED_CHARSET)
+ int index = 0;
+
+ for (int i = 0; i < font.numChars; i++)
+ {
+ if (font.charValues[i] == letter)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ return index;
+#else
+ return (letter - 32);
+#endif
+}
+
+// Load an Image font file (XNA style)
static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
{
#define COLOR_EQUAL(col1, col2) ((col1.r == col2.r)&&(col1.g == col2.g)&&(col1.b == col2.b)&&(col1.a == col2.a))
@@ -503,8 +565,8 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
int x = 0;
int y = 0;
- // Default number of characters expected supported
- #define MAX_FONTCHARS 128
+ // Default number of characters supported
+ #define MAX_FONTCHARS 256
// We allocate a temporal arrays for chars data measures,
// once we get the actual number of chars, we copy data to a sized arrays
@@ -565,15 +627,24 @@ static SpriteFont LoadImageFont(Image image, Color key, int firstChar)
xPosToRead = charSpacing;
}
- free(pixels);
-
TraceLog(DEBUG, "SpriteFont data parsed correctly from image");
+
+ // NOTE: We need to remove key color borders from image to avoid weird
+ // artifacts on texture scaling when using FILTER_BILINEAR or FILTER_TRILINEAR
+ for (int i = 0; i < image.height*image.width; i++) if (COLOR_EQUAL(pixels[i], key)) pixels[i] = BLANK;
+
+ // Create a new image with the processed color data (key color replaced by BLANK)
+ Image fontClear = LoadImageEx(pixels, image.width, image.height);
+
+ free(pixels); // Free pixels array memory
// Create spritefont with all data parsed from image
SpriteFont spriteFont = { 0 };
- spriteFont.texture = LoadTextureFromImage(image); // Convert loaded image to OpenGL texture
+ spriteFont.texture = LoadTextureFromImage(fontClear); // Convert processed image to OpenGL texture
spriteFont.numChars = index;
+
+ UnloadImage(fontClear); // Unload processed image once converted to texture
// We got tempCharValues and tempCharsRecs populated with chars data
// Now we move temp data to sized charValues and charRecs arrays
@@ -803,8 +874,13 @@ static SpriteFont LoadBMFont(const char *fileName)
strncat(texPath, texFileName, strlen(texFileName));
TraceLog(DEBUG, "[%s] Font texture loading path: %s", fileName, texPath);
+
+ Image imFont = LoadImage(texPath);
+
+ if (imFont.format == UNCOMPRESSED_GRAYSCALE) ImageAlphaMask(&imFont, imFont);
- font.texture = LoadTexture(texPath);
+ font.texture = LoadTextureFromImage(imFont);
+
font.size = fontSize;
font.numChars = numChars;
font.charValues = (int *)malloc(numChars*sizeof(int));
@@ -812,22 +888,18 @@ static SpriteFont LoadBMFont(const char *fileName)
font.charOffsets = (Vector2 *)malloc(numChars*sizeof(Vector2));
font.charAdvanceX = (int *)malloc(numChars*sizeof(int));
+ UnloadImage(imFont);
+
free(texPath);
int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
- bool unorderedChars = false;
- int firstChar = 0;
-
for (int i = 0; i < numChars; i++)
{
fgets(buffer, MAX_BUFFER_SIZE, fntFile);
sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i",
&charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX);
- if (i == 0) firstChar = charId;
- else if (i != (charId - firstChar)) unorderedChars = true;
-
// Save data properly in sprite font
font.charValues[i] = charId;
font.charRecs[i] = (Rectangle){ charX, charY, charWidth, charHeight };
@@ -837,11 +909,7 @@ static SpriteFont LoadBMFont(const char *fileName)
fclose(fntFile);
- if (firstChar != FONT_FIRST_CHAR) TraceLog(WARNING, "BMFont not supported: expected SPACE(32) as first character, falling back to default font");
- else if (unorderedChars) TraceLog(WARNING, "BMFont not supported: unordered chars data, falling back to default font");
-
- // NOTE: Font data could be not ordered by charId: 32,33,34,35... raylib does not support unordered BMFonts
- if ((firstChar != FONT_FIRST_CHAR) || (unorderedChars) || (font.texture.id == 0))
+ if (font.texture.id == 0)
{
UnloadSpriteFont(font);
font = GetDefaultFont();
@@ -853,14 +921,17 @@ static SpriteFont LoadBMFont(const char *fileName)
// Generate a sprite font from TTF file data (font size required)
// TODO: Review texture packing method and generation (use oversampling)
-static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int numChars)
+static SpriteFont LoadTTF(const char *fileName, int fontSize, int numChars, int *fontChars)
{
- // NOTE: Generated font uses some hardcoded values
- #define FONT_TEXTURE_WIDTH 512 // Font texture width
- #define FONT_TEXTURE_HEIGHT 512 // Font texture height
+ // NOTE: Font texture size is predicted (being as much conservative as possible)
+ // Predictive method consist of supposing same number of chars by line-column (sqrtf)
+ // and a maximum character width of 3/4 of fontSize... it worked ok with all my tests...
+ int textureSize = GetNextPOT(ceil((float)fontSize*3/4)*ceil(sqrtf((float)numChars)));
+
+ TraceLog(INFO, "TTF spritefont loading: Predicted texture size: %ix%i", textureSize, textureSize);
unsigned char *ttfBuffer = (unsigned char *)malloc(1 << 25);
- unsigned char *dataBitmap = (unsigned char *)malloc(FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT*sizeof(unsigned char)); // One channel bitmap returned!
+ unsigned char *dataBitmap = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)); // One channel bitmap returned!
stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*numChars);
SpriteFont font = { 0 };
@@ -869,40 +940,47 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int
if (ttfFile == NULL)
{
- TraceLog(WARNING, "[%s] FNT file could not be opened", fileName);
+ TraceLog(WARNING, "[%s] TTF file could not be opened", fileName);
return font;
}
fread(ttfBuffer, 1, 1<<25, ttfFile);
+
+ if (fontChars[0] != 32) TraceLog(WARNING, "TTF spritefont loading: first character is not SPACE(32) character");
// NOTE: Using stb_truetype crappy packing method, no guarante the font fits the image...
- stbtt_BakeFontBitmap(ttfBuffer,0, fontSize, dataBitmap, FONT_TEXTURE_WIDTH, FONT_TEXTURE_HEIGHT, firstChar, numChars, charData);
+ // TODO: Replace this function by a proper packing method and support random chars order,
+ // we already receive a list (fontChars) with the ordered expected characters
+ int result = stbtt_BakeFontBitmap(ttfBuffer, 0, fontSize, dataBitmap, textureSize, textureSize, fontChars[0], numChars, charData);
+ //if (result > 0) TraceLog(INFO, "TTF spritefont loading: first unused row of generated bitmap: %i", result);
+ if (result < 0) TraceLog(WARNING, "TTF spritefont loading: Not all the characters fit in the font");
+
free(ttfBuffer);
// Convert image data from grayscale to to UNCOMPRESSED_GRAY_ALPHA
- unsigned char *dataGrayAlpha = (unsigned char *)malloc(FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT*sizeof(unsigned char)*2); // Two channels
- int k = 0;
+ unsigned char *dataGrayAlpha = (unsigned char *)malloc(textureSize*textureSize*sizeof(unsigned char)*2); // Two channels
- for (int i = 0; i < FONT_TEXTURE_WIDTH*FONT_TEXTURE_HEIGHT; i++)
+ for (int i = 0, k = 0; i < textureSize*textureSize; i++, k += 2)
{
dataGrayAlpha[k] = 255;
dataGrayAlpha[k + 1] = dataBitmap[i];
-
- k += 2;
}
free(dataBitmap);
// Sprite font generation from TTF extracted data
Image image;
- image.width = FONT_TEXTURE_WIDTH;
- image.height = FONT_TEXTURE_HEIGHT;
+ image.width = textureSize;
+ image.height = textureSize;
image.mipmaps = 1;
image.format = UNCOMPRESSED_GRAY_ALPHA;
image.data = dataGrayAlpha;
-
+
font.texture = LoadTextureFromImage(image);
+
+ //WritePNG("generated_ttf_image.png", (unsigned char *)image.data, image.width, image.height, 2);
+
UnloadImage(image); // Unloads dataGrayAlpha
font.size = fontSize;
@@ -914,7 +992,7 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int
for (int i = 0; i < font.numChars; i++)
{
- font.charValues[i] = i + firstChar;
+ font.charValues[i] = fontChars[i];
font.charRecs[i].x = (int)charData[i].x0;
font.charRecs[i].y = (int)charData[i].y0;
diff --git a/src/textures.c b/src/textures.c
index 5de0590b..5354a74f 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -8,7 +8,7 @@
* stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
* NOTE: stb_image has been slightly modified, original library: https://github.com/nothings/stb
*
-* Copyright (c) 2014 Ramon Santamaria (@raysan5)
+* Copyright (c) 2014-2016 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.
@@ -229,7 +229,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
// TODO: Review function to support multiple color modes
Image LoadImageFromRES(const char *rresName, int resId)
{
- Image image;
+ Image image = { 0 };
bool found = false;
char id[4]; // rRES file identifier
@@ -442,6 +442,96 @@ void UnloadRenderTexture(RenderTexture2D target)
if (target.id != 0) rlDeleteRenderTextures(target);
}
+// Set texture scaling filter mode
+void SetTextureFilter(Texture2D texture, int filterMode)
+{
+ switch (filterMode)
+ {
+ case FILTER_POINT:
+ {
+ if (texture.mipmaps > 1)
+ {
+ // RL_FILTER_MIP_NEAREST - tex filter: POINT, mipmaps filter: POINT (sharp switching between mipmaps)
+ rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_MIP_NEAREST);
+
+ // RL_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
+ rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_NEAREST);
+ }
+ else
+ {
+ // RL_FILTER_NEAREST - tex filter: POINT (no filter), no mipmaps
+ rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_NEAREST);
+ rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_NEAREST);
+ }
+ } break;
+ case FILTER_BILINEAR:
+ {
+ if (texture.mipmaps > 1)
+ {
+ // RL_FILTER_LINEAR_MIP_NEAREST - tex filter: BILINEAR, mipmaps filter: POINT (sharp switching between mipmaps)
+ // Alternative: RL_FILTER_NEAREST_MIP_LINEAR - tex filter: POINT, mipmaps filter: BILINEAR (smooth transition between mipmaps)
+ rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR_MIP_NEAREST);
+
+ // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
+ rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR);
+ }
+ else
+ {
+ // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
+ rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR);
+ rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR);
+ }
+ } break;
+ case FILTER_TRILINEAR:
+ {
+ if (texture.mipmaps > 1)
+ {
+ // RL_FILTER_MIP_LINEAR - tex filter: BILINEAR, mipmaps filter: BILINEAR (smooth transition between mipmaps)
+ rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_MIP_LINEAR);
+
+ // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
+ rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR);
+ }
+ else
+ {
+ TraceLog(WARNING, "[TEX ID %i] No mipmaps available for TRILINEAR texture filtering", texture.id);
+
+ // RL_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps
+ rlTextureParameters(texture.id, RL_TEXTURE_MIN_FILTER, RL_FILTER_LINEAR);
+ rlTextureParameters(texture.id, RL_TEXTURE_MAG_FILTER, RL_FILTER_LINEAR);
+ }
+ } break;
+ case FILTER_ANISOTROPIC_4X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 4); break;
+ case FILTER_ANISOTROPIC_8X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 8); break;
+ case FILTER_ANISOTROPIC_16X: rlTextureParameters(texture.id, RL_TEXTURE_ANISOTROPIC_FILTER, 16); break;
+ default: break;
+ }
+}
+
+// Set texture wrapping mode
+void SetTextureWrap(Texture2D texture, int wrapMode)
+{
+ switch (wrapMode)
+ {
+ case WRAP_REPEAT:
+ {
+ rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_REPEAT);
+ rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_REPEAT);
+ } break;
+ case WRAP_CLAMP:
+ {
+ rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_CLAMP);
+ rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_CLAMP);
+ } break;
+ case WRAP_MIRROR:
+ {
+ rlTextureParameters(texture.id, RL_TEXTURE_WRAP_S, RL_WRAP_CLAMP_MIRROR);
+ rlTextureParameters(texture.id, RL_TEXTURE_WRAP_T, RL_WRAP_CLAMP_MIRROR);
+ } break;
+ default: break;
+ }
+}
+
// Get pixel data from image in the form of Color struct array
Color *GetImageData(Image image)
{
@@ -694,28 +784,45 @@ void ImageFormat(Image *image, int newFormat)
}
// Apply alpha mask to image
-// NOTE 1: Returned image is RGBA - 32bit
+// NOTE 1: Returned image is GRAY_ALPHA (16bit) or RGBA (32bit)
// NOTE 2: alphaMask should be same size as image
void ImageAlphaMask(Image *image, Image alphaMask)
{
- if (image->format >= COMPRESSED_DXT1_RGB)
+ if ((image->width != alphaMask.width) || (image->height != alphaMask.height))
+ {
+ TraceLog(WARNING, "Alpha mask must be same size as image");
+ }
+ else if (image->format >= COMPRESSED_DXT1_RGB)
{
TraceLog(WARNING, "Alpha mask can not be applied to compressed data formats");
- return;
}
else
{
// Force mask to be Grayscale
Image mask = ImageCopy(alphaMask);
- ImageFormat(&mask, UNCOMPRESSED_GRAYSCALE);
+ if (mask.format != UNCOMPRESSED_GRAYSCALE) ImageFormat(&mask, UNCOMPRESSED_GRAYSCALE);
- // Convert image to RGBA
- if (image->format != UNCOMPRESSED_R8G8B8A8) ImageFormat(image, UNCOMPRESSED_R8G8B8A8);
-
- // Apply alpha mask to alpha channel
- for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4)
+ // In case image is only grayscale, we just add alpha channel
+ if (image->format == UNCOMPRESSED_GRAYSCALE)
{
- ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
+ ImageFormat(image, UNCOMPRESSED_GRAY_ALPHA);
+
+ // Apply alpha mask to alpha channel
+ for (int i = 0, k = 1; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 2)
+ {
+ ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
+ }
+ }
+ else
+ {
+ // Convert image to RGBA
+ if (image->format != UNCOMPRESSED_R8G8B8A8) ImageFormat(image, UNCOMPRESSED_R8G8B8A8);
+
+ // Apply alpha mask to alpha channel
+ for (int i = 0, k = 3; (i < mask.width*mask.height) || (i < image->width*image->height); i++, k += 4)
+ {
+ ((unsigned char *)image->data)[k] = ((unsigned char *)mask.data)[i];
+ }
}
UnloadImage(mask);
@@ -1118,7 +1225,7 @@ Image ImageText(const char *text, int fontSize, Color color)
if (fontSize < defaultFontSize) fontSize = defaultFontSize;
int spacing = fontSize/defaultFontSize;
- Image imText = ImageTextEx(GetDefaultFont(), text, fontSize, spacing, color);
+ Image imText = ImageTextEx(GetDefaultFont(), text, (float)fontSize, spacing, color);
return imText;
}
@@ -1134,7 +1241,7 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing
// NOTE: GetTextureData() not available in OpenGL ES
Image imFont = GetTextureData(font.texture);
- ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Required for color tint
+ ImageFormat(&imFont, UNCOMPRESSED_R8G8B8A8); // Convert to 32 bit for color tint
ImageColorTint(&imFont, tint); // Apply color tint to font
Color *fontPixels = GetImageData(imFont);
@@ -1183,7 +1290,7 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing
void ImageDrawText(Image *dst, Vector2 position, const char *text, int fontSize, Color color)
{
// NOTE: For default font, sapcing is set to desired font size / default font size (10)
- ImageDrawTextEx(dst, position, GetDefaultFont(), text, fontSize, fontSize/10, color);
+ ImageDrawTextEx(dst, position, GetDefaultFont(), text, (float)fontSize, fontSize/10, color);
}
// Draw text (custom sprite font) within an image (destination)
@@ -1317,7 +1424,7 @@ void ImageColorContrast(Image *image, float contrast)
if (contrast < -100) contrast = -100;
if (contrast > 100) contrast = 100;
- contrast = (100.0 + contrast)/100.0;
+ contrast = (100.0f + contrast)/100.0f;
contrast *= contrast;
Color *pixels = GetImageData(*image);
@@ -1326,7 +1433,7 @@ void ImageColorContrast(Image *image, float contrast)
{
for (int x = 0; x < image->width; x++)
{
- float pR = (float)pixels[y*image->width + x].r/255.0;
+ float pR = (float)pixels[y*image->width + x].r/255.0f;
pR -= 0.5;
pR *= contrast;
pR += 0.5;
@@ -1334,7 +1441,7 @@ void ImageColorContrast(Image *image, float contrast)
if (pR < 0) pR = 0;
if (pR > 255) pR = 255;
- float pG = (float)pixels[y*image->width + x].g/255.0;
+ float pG = (float)pixels[y*image->width + x].g/255.0f;
pG -= 0.5;
pG *= contrast;
pG += 0.5;
@@ -1342,7 +1449,7 @@ void ImageColorContrast(Image *image, float contrast)
if (pG < 0) pG = 0;
if (pG > 255) pG = 255;
- float pB = (float)pixels[y*image->width + x].b/255.0;
+ float pB = (float)pixels[y*image->width + x].b/255.0f;
pB -= 0.5;
pB *= contrast;
pB += 0.5;
diff --git a/src/utils.c b/src/utils.c
index 36b06f0f..b96e2c70 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -8,7 +8,7 @@
* tinfl - zlib DEFLATE algorithm decompression lib
* stb_image_write - PNG writting functions
*
-* Copyright (c) 2014 Ramon Santamaria (@raysan5)
+* Copyright (c) 2014-2016 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.