Browse Source

Merge remote-tracking branch 'refs/remotes/raysan5/develop' into develop

victorfisac 8 years ago
parent
commit
80f6b2f963

+ 1 - 1
examples/core_3d_camera_first_person.c

@@ -47,7 +47,7 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);                  // Update camera and player position
+        UpdateCamera(&camera);                  // Update camera
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw

+ 1 - 1
examples/core_3d_camera_free.c

@@ -39,7 +39,7 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);          // Update internal camera and our camera
+        UpdateCamera(&camera);          // Update camera
         
         
         if (IsKeyDown('Z')) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
         if (IsKeyDown('Z')) camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------

+ 3 - 5
examples/core_3d_picking.c

@@ -22,7 +22,7 @@ int main()
 
 
     // Define the camera to look into our 3d world
     // Define the camera to look into our 3d world
     Camera camera;
     Camera camera;
-    camera.position = (Vector3){ 0.0f, 10.0f, 10.0f };  // Camera position
+    camera.position = (Vector3){ 10.0f, 10.0f, 10.0f }; // Camera position
     camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
     camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
     camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
     camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
     camera.fovy = 45.0f;                                // Camera field-of-view Y
     camera.fovy = 45.0f;                                // Camera field-of-view Y
@@ -34,9 +34,7 @@ int main()
     
     
     bool collision = false;
     bool collision = false;
     
     
-    SetCameraMode(CAMERA_FREE);         // Set a free camera mode
-    SetCameraPosition(camera.position); // Set internal camera position to match our camera position
-    SetCameraFovy(camera.fovy);         // Set internal camera field-of-view Y
+    SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode
 
 
     SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
     SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
@@ -46,7 +44,7 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);          // Update internal camera and our camera
+        UpdateCamera(&camera);          // Update camera
         
         
         if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
         if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
         {
         {

+ 8 - 5
examples/core_oculus_rift.c

@@ -30,13 +30,15 @@ int main()
     
     
     // Define the camera to look into our 3d world
     // Define the camera to look into our 3d world
     Camera camera;
     Camera camera;
-    camera.position = (Vector3){ 5.0f, 5.0f, 5.0f };    // Camera position
-    camera.target = (Vector3){ 0.0f, 0.0f, 0.0f };      // Camera looking at point
+    camera.position = (Vector3){ 5.0f, 2.0f, 5.0f };    // Camera position
+    camera.target = (Vector3){ 0.0f, 2.0f, 0.0f };      // Camera looking at point
     camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
     camera.up = (Vector3){ 0.0f, 1.0f, 0.0f };          // Camera up vector (rotation towards target)
     camera.fovy = 60.0f;                                // Camera field-of-view Y
     camera.fovy = 60.0f;                                // Camera field-of-view Y
     
     
     Vector3 cubePosition = { 0.0f, 0.0f, 0.0f };
     Vector3 cubePosition = { 0.0f, 0.0f, 0.0f };
     
     
+    SetCameraMode(camera, CAMERA_FIRST_PERSON);         // Set first person camera mode
+    
     SetTargetFPS(90);                   // Set our game to run at 90 frames-per-second
     SetTargetFPS(90);                   // Set our game to run at 90 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
 
 
@@ -45,9 +47,10 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateVrTracking();
+        if (IsVrSimulator()) UpdateCamera(&camera);     // Update camera (simulator mode)
+        else UpdateVrTracking(&camera);                 // Update camera with device tracking data
         
         
-        if (IsKeyPressed(KEY_SPACE)) ToggleVrMode();
+        if (IsKeyPressed(KEY_SPACE)) ToggleVrMode();    // Toggle VR mode
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw
@@ -61,7 +64,7 @@ int main()
                 DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, RED);
                 DrawCube(cubePosition, 2.0f, 2.0f, 2.0f, RED);
                 DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, MAROON);
                 DrawCubeWires(cubePosition, 2.0f, 2.0f, 2.0f, MAROON);
 
 
-                DrawGrid(10, 1.0f);
+                DrawGrid(40, 1.0f);
 
 
             End3dMode();
             End3dMode();
 
 

+ 3 - 6
examples/core_world_screen.c

@@ -21,16 +21,13 @@ int main()
     InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free");
     InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera free");
 
 
     // Define the camera to look into our 3d world
     // Define the camera to look into our 3d world
-    Camera camera = {{ 0.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f };
+    Camera camera = {{ 10.0f, 10.0f, 10.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 1.0f, 0.0f }, 45.0f };
 
 
     Vector3 cubePosition = { 0.0f, 0.0f, 0.0f };
     Vector3 cubePosition = { 0.0f, 0.0f, 0.0f };
     
     
     Vector2 cubeScreenPosition;
     Vector2 cubeScreenPosition;
     
     
-    SetCameraMode(CAMERA_FREE);         // Set a free camera mode
-    SetCameraPosition(camera.position); // Set internal camera position to match our camera position
-    SetCameraTarget(camera.target);     // Set internal camera target to match our camera target
-    SetCameraFovy(camera.fovy);         // Set internal camera field-of-view Y
+    SetCameraMode(camera, CAMERA_FREE); // Set a free camera mode
 
 
     SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
     SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
@@ -40,7 +37,7 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);          // Update internal camera and our camera
+        UpdateCamera(&camera);          // Update camera
         
         
         // Calculate cube screen space position (with a little offset to be in top)
         // Calculate cube screen space position (with a little offset to be in top)
         cubeScreenPosition = GetWorldToScreen((Vector3){cubePosition.x, cubePosition.y + 2.5f, cubePosition.z}, camera);
         cubeScreenPosition = GetWorldToScreen((Vector3){cubePosition.x, cubePosition.y + 2.5f, cubePosition.z}, camera);

+ 4 - 7
examples/models_billboard.c

@@ -26,20 +26,17 @@ int main()
     Texture2D bill = LoadTexture("resources/billboard.png");     // Our texture billboard
     Texture2D bill = LoadTexture("resources/billboard.png");     // Our texture billboard
     Vector3 billPosition = { 0.0f, 2.0f, 0.0f };                 // Position where draw billboard
     Vector3 billPosition = { 0.0f, 2.0f, 0.0f };                 // Position where draw billboard
     
     
-    SetCameraMode(CAMERA_ORBITAL);      // Set an orbital camera mode
-    SetCameraPosition(camera.position); // Set internal camera position to match our camera position
-    SetCameraTarget(camera.target);     // Set internal camera target to match our camera target
-    SetCameraFovy(camera.fovy);         // Set internal camera field-of-view Y
+    SetCameraMode(camera, CAMERA_ORBITAL);  // Set an orbital camera mode
 
 
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
 
 
     // Main game loop
     // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
+    while (!WindowShouldClose())            // Detect window close button or ESC key
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);          // Update internal camera and our camera
+        UpdateCamera(&camera);              // Update camera
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw

+ 1 - 1
examples/models_cubicmap.c

@@ -45,7 +45,7 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);              // Update internal camera and our camera
+        UpdateCamera(&camera);              // Update camera
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw

+ 5 - 6
examples/models_heightmap.c

@@ -29,20 +29,19 @@ int main()
     map.material.texDiffuse = texture;                          // Set map diffuse texture
     map.material.texDiffuse = texture;                          // Set map diffuse texture
     Vector3 mapPosition = { -8.0f, 0.0f, -8.0f };               // Set model position (depends on model scaling!)
     Vector3 mapPosition = { -8.0f, 0.0f, -8.0f };               // Set model position (depends on model scaling!)
 
 
-    UnloadImage(image);                 // Unload heightmap image from RAM, already uploaded to VRAM
+    UnloadImage(image);                     // Unload heightmap image from RAM, already uploaded to VRAM
     
     
-    SetCameraMode(CAMERA_ORBITAL);      // Set an orbital camera mode
-    SetCameraPosition(camera.position); // Set internal camera position to match our custom camera position
+    SetCameraMode(camera, CAMERA_ORBITAL);  // Set an orbital camera mode
 
 
-    SetTargetFPS(60);                   // Set our game to run at 60 frames-per-second
+    SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
 
 
     // Main game loop
     // Main game loop
-    while (!WindowShouldClose())        // Detect window close button or ESC key
+    while (!WindowShouldClose())            // Detect window close button or ESC key
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);          // Update internal camera and our camera
+        UpdateCamera(&camera);              // Update camera
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw

+ 4 - 13
examples/resources/shaders/glsl100/standard.fs

@@ -38,7 +38,6 @@ uniform Light lights[maxLights];
 
 
 vec3 ComputeLightPoint(Light l, vec3 n, vec3 v, float s)
 vec3 ComputeLightPoint(Light l, vec3 n, vec3 v, float s)
 {
 {
-/*
     vec3 surfacePos = vec3(modelMatrix*vec4(fragPosition, 1.0));
     vec3 surfacePos = vec3(modelMatrix*vec4(fragPosition, 1.0));
     vec3 surfaceToLight = l.position - surfacePos;
     vec3 surfaceToLight = l.position - surfacePos;
     
     
@@ -51,17 +50,14 @@ vec3 ComputeLightPoint(Light l, vec3 n, vec3 v, float s)
     if (diff > 0.0)
     if (diff > 0.0)
     {
     {
         vec3 h = normalize(-l.direction + v);
         vec3 h = normalize(-l.direction + v);
-        spec = pow(dot(n, h), 3.0 + glossiness)*s;
+        spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;
     }
     }
     
     
     return (diff*l.diffuse.rgb + spec*colSpecular.rgb);
     return (diff*l.diffuse.rgb + spec*colSpecular.rgb);
-*/
-    return vec3(0.5);
 }
 }
 
 
 vec3 ComputeLightDirectional(Light l, vec3 n, vec3 v, float s)
 vec3 ComputeLightDirectional(Light l, vec3 n, vec3 v, float s)
 {
 {
-/*
     vec3 lightDir = normalize(-l.direction);
     vec3 lightDir = normalize(-l.direction);
     
     
     // Diffuse shading
     // Diffuse shading
@@ -72,18 +68,15 @@ vec3 ComputeLightDirectional(Light l, vec3 n, vec3 v, float s)
     if (diff > 0.0)
     if (diff > 0.0)
     {
     {
         vec3 h = normalize(lightDir + v);
         vec3 h = normalize(lightDir + v);
-        spec = pow(dot(n, h), 3.0 + glossiness)*s;
+        spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;
     }
     }
     
     
     // Combine results
     // Combine results
     return (diff*l.intensity*l.diffuse.rgb + spec*colSpecular.rgb);
     return (diff*l.intensity*l.diffuse.rgb + spec*colSpecular.rgb);
-*/
-    return vec3(0.5);
 }
 }
 
 
 vec3 ComputeLightSpot(Light l, vec3 n, vec3 v, float s)
 vec3 ComputeLightSpot(Light l, vec3 n, vec3 v, float s)
 {
 {
-/*
     vec3 surfacePos = vec3(modelMatrix*vec4(fragPosition, 1));
     vec3 surfacePos = vec3(modelMatrix*vec4(fragPosition, 1));
     vec3 lightToSurface = normalize(surfacePos - l.position);
     vec3 lightToSurface = normalize(surfacePos - l.position);
     vec3 lightDir = normalize(-l.direction);
     vec3 lightDir = normalize(-l.direction);
@@ -108,12 +101,10 @@ vec3 ComputeLightSpot(Light l, vec3 n, vec3 v, float s)
     if (diffAttenuation > 0.0)
     if (diffAttenuation > 0.0)
     {
     {
         vec3 h = normalize(lightDir + v);
         vec3 h = normalize(lightDir + v);
-        spec = pow(dot(n, h), 3.0 + glossiness)*s;
+        spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;
     }
     }
     
     
     return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));
     return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));
-*/
-    return vec3(0.5);
 }
 }
 
 
 void main()
 void main()
@@ -140,7 +131,7 @@ void main()
     
     
     // Calculate specular texture color fetching or set to maximum specular value by default
     // Calculate specular texture color fetching or set to maximum specular value by default
     float spec = 1.0;
     float spec = 1.0;
-    if (useSpecular == 1) spec *= normalize(texture2D(texture2, fragTexCoord).r);
+    if (useSpecular == 1) spec = texture2D(texture2, fragTexCoord).r;
     
     
     for (int i = 0; i < maxLights; i++)
     for (int i = 0; i < maxLights; i++)
     {
     {

+ 4 - 4
examples/resources/shaders/glsl330/standard.fs

@@ -50,7 +50,7 @@ vec3 ComputeLightPoint(Light l, vec3 n, vec3 v, float s)
     if (diff > 0.0)
     if (diff > 0.0)
     {
     {
         vec3 h = normalize(-l.direction + v);
         vec3 h = normalize(-l.direction + v);
-        spec = pow(dot(n, h), 3.0 + glossiness)*s;
+        spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;
     }
     }
     
     
     return (diff*l.diffuse.rgb + spec*colSpecular.rgb);
     return (diff*l.diffuse.rgb + spec*colSpecular.rgb);
@@ -68,7 +68,7 @@ vec3 ComputeLightDirectional(Light l, vec3 n, vec3 v, float s)
     if (diff > 0.0)
     if (diff > 0.0)
     {
     {
         vec3 h = normalize(lightDir + v);
         vec3 h = normalize(lightDir + v);
-        spec = pow(dot(n, h), 3.0 + glossiness)*s;
+        spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;
     }
     }
     
     
     // Combine results
     // Combine results
@@ -101,7 +101,7 @@ vec3 ComputeLightSpot(Light l, vec3 n, vec3 v, float s)
     if (diffAttenuation > 0.0)
     if (diffAttenuation > 0.0)
     {
     {
         vec3 h = normalize(lightDir + v);
         vec3 h = normalize(lightDir + v);
-        spec = pow(dot(n, h), 3.0 + glossiness)*s;
+        spec = pow(abs(dot(n, h)), 3.0 + glossiness)*s;
     }
     }
     
     
     return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));
     return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));
@@ -131,7 +131,7 @@ void main()
     
     
     // Calculate specular texture color fetching or set to maximum specular value by default
     // Calculate specular texture color fetching or set to maximum specular value by default
     float spec = 1.0;
     float spec = 1.0;
-    if (useSpecular == 1) spec *= normalize(texture(texture2, fragTexCoord).r);
+    if (useSpecular == 1) spec = texture(texture2, fragTexCoord).r;
     
     
     for (int i = 0; i < maxLights; i++)
     for (int i = 0; i < maxLights; i++)
     {
     {

+ 2 - 4
examples/shaders_custom_uniform.c

@@ -51,9 +51,7 @@ int main()
     RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
     RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
     
     
     // Setup orbital camera
     // Setup orbital camera
-    SetCameraMode(CAMERA_ORBITAL);          // Set an orbital camera mode
-    SetCameraPosition(camera.position);     // Set internal camera position to match our camera position
-    SetCameraTarget(camera.target);         // Set internal camera target to match our camera target
+    SetCameraMode(camera, CAMERA_ORBITAL);  // Set an orbital camera mode
 
 
     SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
@@ -71,7 +69,7 @@ int main()
         // Send new value to the shader to be used on drawing
         // Send new value to the shader to be used on drawing
         SetShaderValue(shader, swirlCenterLoc, swirlCenter, 2);
         SetShaderValue(shader, swirlCenterLoc, swirlCenter, 2);
         
         
-        UpdateCamera(&camera);              // Update internal camera and our camera
+        UpdateCamera(&camera);              // Update camera
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw

+ 4 - 1
examples/shaders_model_shader.c

@@ -42,7 +42,7 @@ int main()
     
     
     Vector3 position = { 0.0f, 0.0f, 0.0f };    // Set model position
     Vector3 position = { 0.0f, 0.0f, 0.0f };    // Set model position
     
     
-    SetCameraMode(camera, CAMERA_ORBITAL);      // Set an orbital camera mode
+    SetCameraMode(camera, CAMERA_FREE);      // Set an orbital camera mode
 
 
     SetTargetFPS(60);                           // Set our game to run at 60 frames-per-second
     SetTargetFPS(60);                           // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
@@ -70,6 +70,9 @@ int main()
             End3dMode();
             End3dMode();
             
             
             DrawText("(c) Dwarf 3D model by David Moreno", screenWidth - 200, screenHeight - 20, 10, GRAY);
             DrawText("(c) Dwarf 3D model by David Moreno", screenWidth - 200, screenHeight - 20, 10, GRAY);
+            
+            DrawText(FormatText("Camera position: (%.2f, %.2f, %.2f)", camera.position.x, camera.position.y, camera.position.z), 600, 20, 10, BLACK);
+            DrawText(FormatText("Camera target: (%.2f, %.2f, %.2f)", camera.target.x, camera.target.y, camera.target.z), 600, 40, 10, GRAY);
 
 
             DrawFPS(10, 10);
             DrawFPS(10, 10);
 
 

+ 3 - 5
examples/shaders_postprocessing.c

@@ -45,9 +45,7 @@ int main()
     RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
     RenderTexture2D target = LoadRenderTexture(screenWidth, screenHeight);
     
     
     // Setup orbital camera
     // Setup orbital camera
-    SetCameraMode(CAMERA_ORBITAL);          // Set an orbital camera mode
-    SetCameraPosition(camera.position);     // Set internal camera position to match our camera position
-    SetCameraTarget(camera.target);         // Set internal camera target to match our camera target
+    SetCameraMode(camera, CAMERA_ORBITAL);  // Set an orbital camera mode
 
 
     SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
@@ -57,7 +55,7 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);              // Update internal camera and our camera
+        UpdateCamera(&camera);              // Update camera
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw
@@ -67,7 +65,7 @@ int main()
             ClearBackground(RAYWHITE);
             ClearBackground(RAYWHITE);
 
 
             BeginTextureMode(target);   // Enable drawing to texture
             BeginTextureMode(target);   // Enable drawing to texture
-
+            
                 Begin3dMode(camera);
                 Begin3dMode(camera);
 
 
                     DrawModel(dwarf, position, 2.0f, WHITE);   // Draw 3d model with texture
                     DrawModel(dwarf, position, 2.0f, WHITE);   // Draw 3d model with texture

+ 2 - 4
examples/shaders_standard_lighting.c

@@ -64,9 +64,7 @@ int main()
     pointLight->radius = 3.0f;
     pointLight->radius = 3.0f;
 
 
     // Setup orbital camera
     // Setup orbital camera
-    SetCameraMode(CAMERA_ORBITAL);          // Set an orbital camera mode
-    SetCameraPosition(camera.position);     // Set internal camera position to match our camera position
-    SetCameraTarget(camera.target);         // Set internal camera target to match our camera target
+    SetCameraMode(camera, CAMERA_ORBITAL);  // Set an orbital camera mode
 
 
     SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     SetTargetFPS(60);                       // Set our game to run at 60 frames-per-second
     //--------------------------------------------------------------------------------------
     //--------------------------------------------------------------------------------------
@@ -76,7 +74,7 @@ int main()
     {
     {
         // Update
         // Update
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
-        UpdateCamera(&camera);              // Update internal camera and our camera
+        UpdateCamera(&camera);              // Update camera
         //----------------------------------------------------------------------------------
         //----------------------------------------------------------------------------------
 
 
         // Draw
         // Draw

+ 61 - 3
src/audio.c

@@ -79,6 +79,10 @@
 #define JAR_MOD_IMPLEMENTATION
 #define JAR_MOD_IMPLEMENTATION
 #include "external/jar_mod.h"       // MOD loading functions
 #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
 #ifdef _MSC_VER
     #undef bool
     #undef bool
 #endif
 #endif
@@ -98,12 +102,13 @@
 // Types and Structures Definition
 // 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)
 // Music type (file streaming from memory)
 typedef struct MusicData {
 typedef struct MusicData {
     MusicContextType ctxType;           // Type of music context (OGG, XM, MOD)
     MusicContextType ctxType;           // Type of music context (OGG, XM, MOD)
     stb_vorbis *ctxOgg;                 // OGG audio context
     stb_vorbis *ctxOgg;                 // OGG audio context
+    drflac *ctxFlac;                    // FLAC audio context
     jar_xm_context_t *ctxXm;            // XM chiptune context
     jar_xm_context_t *ctxXm;            // XM chiptune context
     jar_mod_context_t ctxMod;           // MOD 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 LoadWAV(const char *fileName);          // Load WAV file
 static Wave LoadOGG(const char *fileName);          // Load OGG file
 static Wave LoadOGG(const char *fileName);          // Load OGG file
+static Wave LoadFLAC(const char *fileName);         // Load FLAC file
 
 
 #if defined(AUDIO_STANDALONE)
 #if defined(AUDIO_STANDALONE)
 const char *GetExtension(const char *fileName);     // Get the extension for a filename
 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);
     if (strcmp(GetExtension(fileName), "wav") == 0) wave = LoadWAV(fileName);
     else if (strcmp(GetExtension(fileName), "ogg") == 0) wave = LoadOGG(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);
     else TraceLog(WARNING, "[%s] File extension not recognized, it can't be loaded", fileName);
 
 
     return wave;
     return wave;
@@ -672,7 +679,7 @@ Music LoadMusicStream(const char *fileName)
         // Open ogg audio stream
         // Open ogg audio stream
         music->ctxOgg = stb_vorbis_open_filename(fileName, NULL, NULL);
         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
         else
         {
         {
             stb_vorbis_info info = stb_vorbis_get_info(music->ctxOgg);  // Get Ogg file info
             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)
     else if (strcmp(GetExtension(fileName), "xm") == 0)
     {
     {
         int result = jar_xm_create_context_from_file(&music->ctxXm, 48000, fileName);
         int result = jar_xm_create_context_from_file(&music->ctxXm, 48000, fileName);
@@ -739,6 +764,7 @@ void UnloadMusicStream(Music music)
     CloseAudioStream(music->stream);
     CloseAudioStream(music->stream);
 
 
     if (music->ctxType == MUSIC_AUDIO_OGG) stb_vorbis_close(music->ctxOgg);
     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_XM) jar_xm_free_context(music->ctxXm);
     else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod);
     else if (music->ctxType == MUSIC_MODULE_MOD) jar_mod_unload(&music->ctxMod);
 
 
@@ -818,6 +844,20 @@ void UpdateMusicStream(Music music)
                     UpdateAudioStream(music->stream, pcm, numSamplesOgg*music->stream.channels);
                     UpdateAudioStream(music->stream, pcm, numSamplesOgg*music->stream.channels);
                     music->samplesLeft -= (numSamplesOgg*music->stream.channels);
                     music->samplesLeft -= (numSamplesOgg*music->stream.channels);
 
 
+                } break;
+                case MUSIC_AUDIO_FLAC:
+                {
+                    if (music->samplesLeft >= AUDIO_BUFFER_SIZE) numSamples = AUDIO_BUFFER_SIZE;
+                    else numSamples = music->samplesLeft;
+                    
+                    int pcmi[AUDIO_BUFFER_SIZE];
+                    
+                    // NOTE: Returns the number of samples to process (should be the same as numSamples)
+                    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;
                 } break;
                 case MUSIC_MODULE_XM:
                 case MUSIC_MODULE_XM:
                 {
                 {
@@ -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);
         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.sampleCount = totalSamples;
 
 
         wave.data = (short *)malloc(totalSamplesLength*sizeof(short));
         wave.data = (short *)malloc(totalSamplesLength*sizeof(short));
@@ -1214,6 +1254,24 @@ static Wave LoadOGG(const char *fileName)
     return wave;
     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
 // Some required functions for audio standalone module version
 #if defined(AUDIO_STANDALONE)
 #if defined(AUDIO_STANDALONE)
 // Get the extension for a filename
 // Get the extension for a filename

+ 243 - 196
src/core.c

@@ -18,7 +18,11 @@
 *
 *
 *   On PLATFORM_RPI, graphic device is managed by EGL and input system is coded in raw mode.
 *   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
 *   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.
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -54,12 +58,12 @@
 #endif
 #endif
 
 
 #include <stdio.h>          // Standard input / output lib
 #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)
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
     //#define GLFW_INCLUDE_NONE   // Disable the standard OpenGL header inclusion on GLFW3
     //#define GLFW_INCLUDE_NONE   // Disable the standard OpenGL header inclusion on GLFW3
@@ -75,8 +79,7 @@
 #endif
 #endif
 
 
 #if defined(PLATFORM_ANDROID)
 #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/window.h>             // Defines AWINDOW_FLAG_FULLSCREEN and others
     #include <android_native_app_glue.h>    // Defines basic app state struct and manages activity
     #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 DEFAULT_GAMEPAD_DEV     "/dev/input/eventN"
 
 
     #define MOUSE_SENSITIVITY         0.8f
     #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
 #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
 // 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 windowReady = false;                // Used to detect display initialization
 static bool appEnabled = true;                  // Used to detec if app is active
 static bool appEnabled = true;                  // Used to detec if app is active
 static bool contextRebindRequired = false;      // Used to know context rebind required
 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
 #endif
 
 
 #if defined(PLATFORM_RPI)
 #if defined(PLATFORM_RPI)
@@ -168,15 +170,11 @@ static int defaultKeyboardMode;                 // Used to store default keyboar
 // Mouse input variables
 // Mouse input variables
 static int mouseStream = -1;                    // Mouse device file descriptor
 static int mouseStream = -1;                    // Mouse device file descriptor
 static bool mouseReady = false;                 // Flag to know if mouse is ready
 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
 // 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
 #endif
 
 
 #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI)
 #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)
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_RPI) || defined(PLATFORM_WEB)
 static const char *windowTitle;             // Window text title...
 static const char *windowTitle;             // Window text title...
 static bool cursorOnScreen = false;         // Tracks if cursor is inside client area
 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 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 mousePosition;               // Mouse position on screen
 static Vector2 touchPosition[MAX_TOUCH_POINTS]; // Touch 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)
 // 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 LoadDefaultFont(void);          // [Module: text] Loads default font on InitWindow()
 extern void UnloadDefaultFont(void);        // [Module: text] Unloads default font from GPU memory
 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
 // 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)
     // Init graphics device (display device and OpenGL context)
     InitGraphicsDevice(width, height);
     InitGraphicsDevice(width, height);
 
 
-    // Load default font for convenience
+#if defined(RL_LOAD_DEFAULT_FONT)
+    // Load default font
     // NOTE: External function (defined in module: text)
     // NOTE: External function (defined in module: text)
     LoadDefaultFont();
     LoadDefaultFont();
+#endif
 
 
     // Init hi-res timer
     // Init hi-res timer
     InitTimer();
     InitTimer();
@@ -355,7 +361,7 @@ void InitWindow(int width, int height, const char *title)
 
 
 #if defined(PLATFORM_ANDROID)
 #if defined(PLATFORM_ANDROID)
 // Android activity initialization
 // 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)");
     TraceLog(INFO, "Initializing raylib (v1.6.0)");
 
 
@@ -364,7 +370,7 @@ void InitWindow(int width, int height, struct android_app *state)
     screenWidth = width;
     screenWidth = width;
     screenHeight = height;
     screenHeight = height;
 
 
-    app = state;
+    app = (struct android_app *)state;
     internalDataPath = app->activity->internalDataPath;
     internalDataPath = app->activity->internalDataPath;
 
 
     // Set desired windows flags before initializing anything
     // 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
 // Close Window and Terminate Context
 void CloseWindow(void)
 void CloseWindow(void)
 {
 {
+#if defined(RL_LOAD_DEFAULT_FONT)
     UnloadDefaultFont();
     UnloadDefaultFont();
+#endif
 
 
     rlglClose();                // De-init rlgl
     rlglClose();                // De-init rlgl
 
 
@@ -456,6 +464,9 @@ void CloseWindow(void)
     // Wait for mouse and gamepad threads to finish before closing
     // Wait for mouse and gamepad threads to finish before closing
     // NOTE: Those threads should already have finished at this point
     // NOTE: Those threads should already have finished at this point
     // because they are controlled by windowShouldClose variable
     // 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(mouseThreadId, NULL);
     pthread_join(gamepadThreadId, NULL);
     pthread_join(gamepadThreadId, NULL);
 #endif
 #endif
@@ -516,6 +527,65 @@ int GetScreenHeight(void)
     return screenHeight;
     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
 // Sets Background Color
 void ClearBackground(Color color)
 void ClearBackground(Color color)
 {
 {
@@ -1034,7 +1104,6 @@ Matrix GetCameraMatrix(Camera camera)
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions
 // 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
 // Detect if a key has been pressed once
 bool IsKeyPressed(int key)
 bool IsKeyPressed(int key)
 {
 {
@@ -1057,7 +1126,7 @@ bool IsKeyDown(int key)
 bool IsKeyReleased(int key)
 bool IsKeyReleased(int key)
 {
 {
     bool released = false;
     bool released = false;
-
+    
     if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 0)) released = true;
     if ((currentKeyState[key] != previousKeyState[key]) && (currentKeyState[key] == 0)) released = true;
     else released = false;
     else released = false;
 
 
@@ -1081,99 +1150,62 @@ int GetKeyPressed(void)
 // NOTE: default exitKey is ESCAPE
 // NOTE: default exitKey is ESCAPE
 void SetExitKey(int key)
 void SetExitKey(int key)
 {
 {
+#if !defined(PLATFORM_ANDROID)
     exitKey = key;
     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
 #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
 #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
 #else
-    if (glfwJoystickPresent(gamepad) == 1) result = true;
+    return NULL;
 #endif
 #endif
+}
 
 
-    return result;
+// Return gamepad axis count
+int GetGamepadAxisCount(int gamepad)
+{
+    return gamepadAxisCount;
 }
 }
 
 
 // Return axis movement vector for a gamepad
 // Return axis movement vector for a gamepad
 float GetGamepadAxisMovement(int gamepad, int axis)
 float GetGamepadAxisMovement(int gamepad, int axis)
 {
 {
     float value = 0;
     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
 #endif
 
 
     return value;
     return value;
@@ -1184,14 +1216,11 @@ bool IsGamepadButtonPressed(int gamepad, int button)
 {
 {
     bool pressed = false;
     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;
     return pressed;
 }
 }
@@ -1201,18 +1230,9 @@ bool IsGamepadButtonDown(int gamepad, int button)
 {
 {
     bool result = false;
     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
 #endif
 
 
     return result;
     return result;
@@ -1222,15 +1242,12 @@ bool IsGamepadButtonDown(int gamepad, int button)
 bool IsGamepadButtonReleased(int gamepad, int button)
 bool IsGamepadButtonReleased(int gamepad, int button)
 {
 {
     bool released = false;
     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;
     return released;
 }
 }
@@ -1240,24 +1257,19 @@ bool IsGamepadButtonUp(int gamepad, int button)
 {
 {
     bool result = false;
     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
 #endif
 
 
     return result;
     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
 // Detect if a mouse button has been pressed once
 bool IsMouseButtonPressed(int button)
 bool IsMouseButtonPressed(int button)
@@ -1411,37 +1423,6 @@ Vector2 GetTouchPosition(int index)
     return position;
     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
 // Module specific Functions Definition
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
@@ -1924,8 +1905,9 @@ static bool GetKeyStatus(int key)
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
     return glfwGetKey(window, key);
     return glfwGetKey(window, key);
 #elif defined(PLATFORM_ANDROID)
 #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)
 #elif defined(PLATFORM_RPI)
     // NOTE: Keys states are filled in PollInputEvents()
     // NOTE: Keys states are filled in PollInputEvents()
     if (key < 0 || key > 511) return false;
     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
     // NOTE: Gestures update must be called every frame to reset gestures correctly
     // because ProcessGestureEvent() is just called on an event, not every frame
     // because ProcessGestureEvent() is just called on an event, not every frame
     UpdateGestures();
     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)
 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB)
     // Mouse input polling
     // Mouse input polling
@@ -1963,9 +1952,8 @@ static void PollInputEvents(void)
 
 
     mousePosition.x = (float)mouseX;
     mousePosition.x = (float)mouseX;
     mousePosition.y = (float)mouseY;
     mousePosition.y = (float)mouseY;
-
+    
     // Keyboard input polling (automatically managed by GLFW3 through callback)
     // Keyboard input polling (automatically managed by GLFW3 through callback)
-    lastKeyPressed = -1;
 
 
     // Register previous keys states
     // Register previous keys states
     for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
     for (int i = 0; i < 512; i++) previousKeyState[i] = currentKeyState[i];
@@ -1975,13 +1963,62 @@ static void PollInputEvents(void)
 
 
     previousMouseWheelY = currentMouseWheelY;
     previousMouseWheelY = currentMouseWheelY;
     currentMouseWheelY = 0;
     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
 #endif
 
 
 #if defined(PLATFORM_ANDROID)
 #if defined(PLATFORM_ANDROID)
     // Register previous keys states
     // 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)
     // Poll Events (registered events)
     // NOTE: Activity is paused if not enabled (appEnabled)
     // 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)
                     // Init graphics device (display device and OpenGL context)
                     InitGraphicsDevice(screenWidth, screenHeight);
                     InitGraphicsDevice(screenWidth, screenHeight);
 
 
-                    // Load default font for convenience
+                    #if defined(RL_LOAD_DEFAULT_FONT)
+                    // Load default font
                     // NOTE: External function (defined in module: text)
                     // NOTE: External function (defined in module: text)
                     LoadDefaultFont();
                     LoadDefaultFont();
+                    #endif
 
 
                     // TODO: GPU assets reload in case of lost focus (lost context)
                     // TODO: GPU assets reload in case of lost focus (lost context)
                     // NOTE: This problem has been solved just unbinding and rebinding context from display
                     // 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);
         //int32_t AKeyEvent_getMetaState(event);
 
 
         // Save current button and its state
         // 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)
         if (keycode == AKEYCODE_POWER)
         {
         {
@@ -2802,7 +2847,9 @@ static void *GamepadThread(void *arg)
                     if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
                     if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS)
                     {
                     {
                         // 1 - button pressed, 0 - button released
                         // 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)
                 else if (gamepadEvent.type == JS_EVENT_AXIS)
@@ -2812,7 +2859,7 @@ static void *GamepadThread(void *arg)
                     if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
                     if (gamepadEvent.number < MAX_GAMEPAD_AXIS)
                     {
                     {
                         // NOTE: Scaling of gamepadEvent.value to get values between -1..1
                         // 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;
                     }
                     }
                 }
                 }
             }
             }

+ 4395 - 0
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 - [email protected]
+
+// 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/>
+*/

+ 5 - 0
src/external/openal_soft/include/AL/alext.h

@@ -431,6 +431,11 @@ ALC_API ALCboolean ALC_APIENTRY alcResetDeviceSOFT(ALCdevice *device, const ALCi
 #endif
 #endif
 #endif
 #endif
 
 
+#ifndef AL_SOFT_gain_clamp_ex
+#define AL_SOFT_gain_clamp_ex 1
+#define AL_GAIN_LIMIT_SOFT                       0x200E
+#endif
+
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }
 #endif
 #endif

BIN
src/external/openal_soft/lib/win32/libOpenAL32.a


+ 0 - 0
src/external/openal_soft/lib/win32/libOpenAL32.dll.a → src/external/openal_soft/lib/win32/libOpenAL32dll.a


+ 62 - 62
src/models.c

@@ -4,7 +4,7 @@
 *
 *
 *   Basic functions to draw 3d shapes and load/draw 3d models (.OBJ)
 *   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
 *   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.
 *   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);
                 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();
         rlEnd();
     rlPopMatrix();
     rlPopMatrix();
@@ -325,25 +325,25 @@ void DrawSphereEx(Vector3 centerPos, float radius, int rings, int slices, Color
             {
             {
                 for (int j = 0; j < slices; j++)
                 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();
         rlEnd();
@@ -364,26 +364,26 @@ void DrawSphereWires(Vector3 centerPos, float radius, int rings, int slices, Col
             {
             {
                 for (int j = 0; j < slices; j++)
                 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();
         rlEnd();
@@ -407,21 +407,21 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
                 // Draw Body -------------------------------------------------------------------------------------
                 // Draw Body -------------------------------------------------------------------------------------
                 for (int i = 0; i < 360; i += 360/sides)
                 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 --------------------------------------------------------------------------------------
                 // Draw Cap --------------------------------------------------------------------------------------
                 for (int i = 0; i < 360; i += 360/sides)
                 for (int i = 0; i < 360; i += 360/sides)
                 {
                 {
                     rlVertex3f(0, height, 0);
                     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
             else
@@ -430,8 +430,8 @@ void DrawCylinder(Vector3 position, float radiusTop, float radiusBottom, float h
                 for (int i = 0; i < 360; i += 360/sides)
                 for (int i = 0; i < 360; i += 360/sides)
                 {
                 {
                     rlVertex3f(0, height, 0);
                     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)
             for (int i = 0; i < 360; i += 360/sides)
             {
             {
                 rlVertex3f(0, 0, 0);
                 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();
         rlEnd();
     rlPopMatrix();
     rlPopMatrix();
@@ -460,17 +460,17 @@ void DrawCylinderWires(Vector3 position, float radiusTop, float radiusBottom, fl
 
 
             for (int i = 0; i < 360; i += 360/sides)
             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();
         rlEnd();
     rlPopMatrix();
     rlPopMatrix();

+ 83 - 57
src/raylib.h

@@ -77,10 +77,6 @@
     #define PLATFORM_DESKTOP
     #define PLATFORM_DESKTOP
 #endif
 #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)
 #if defined(_WIN32) && defined(BUILDING_DLL)
     #define RLAPI __declspec(dllexport)         // We are building raylib as a Win32 DLL
     #define RLAPI __declspec(dllexport)         // We are building raylib as a Win32 DLL
 #elif defined(_WIN32) && defined(RAYLIB_DLL)
 #elif defined(_WIN32) && defined(RAYLIB_DLL)
@@ -93,7 +89,7 @@
 // Some basic Defines
 // Some basic Defines
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 #ifndef PI
 #ifndef PI
-    #define PI 3.14159265358979323846
+    #define PI 3.14159265358979323846f
 #endif
 #endif
 
 
 #define DEG2RAD (PI/180.0f)
 #define DEG2RAD (PI/180.0f)
@@ -174,15 +170,18 @@
 #define KEY_Y                89
 #define KEY_Y                89
 #define KEY_Z                90
 #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
 // Mouse Buttons
 #define MOUSE_LEFT_BUTTON     0
 #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
 // Touch points registered
 #define MAX_TOUCH_POINTS     2
 #define MAX_TOUCH_POINTS     2
@@ -190,24 +189,35 @@
 // Gamepad Number
 // Gamepad Number
 #define GAMEPAD_PLAYER1       0
 #define GAMEPAD_PLAYER1       0
 #define GAMEPAD_PLAYER2       1
 #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_R1       7
+#define GAMEPAD_PS3_BUTTON_L2       4
 #define GAMEPAD_PS3_BUTTON_R2       5
 #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_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
 // Xbox360 USB Controller Buttons
 #define GAMEPAD_XBOX_BUTTON_A       0
 #define GAMEPAD_XBOX_BUTTON_A       0
@@ -218,32 +228,31 @@
 #define GAMEPAD_XBOX_BUTTON_RB      5
 #define GAMEPAD_XBOX_BUTTON_RB      5
 #define GAMEPAD_XBOX_BUTTON_SELECT  6
 #define GAMEPAD_XBOX_BUTTON_SELECT  6
 #define GAMEPAD_XBOX_BUTTON_START   7
 #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)
 #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_X   3
     #define GAMEPAD_XBOX_AXIS_RIGHT_Y   4
     #define GAMEPAD_XBOX_AXIS_RIGHT_Y   4
     #define GAMEPAD_XBOX_AXIS_LT        2
     #define GAMEPAD_XBOX_AXIS_LT        2
     #define GAMEPAD_XBOX_AXIS_RT        5
     #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
 #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)
 // NOTE: MSC C++ compiler does not support compound literals (C99 feature)
 // Plain structures in C++ (without constructors) can be initialized from { } initializers.
 // Plain structures in C++ (without constructors) can be initialized from { } initializers.
@@ -540,6 +549,21 @@ typedef enum {
     COMPRESSED_ASTC_8x8_RGBA        // 2 bpp
     COMPRESSED_ASTC_8x8_RGBA        // 2 bpp
 } TextureFormat;
 } 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)
 // Color blending modes (pre-defined)
 typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode;
 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)
 // Window and Graphics Device Functions (Module: core)
 //------------------------------------------------------------------------------------
 //------------------------------------------------------------------------------------
 #if defined(PLATFORM_ANDROID)
 #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)
 #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
 RLAPI void InitWindow(int width, int height, const char *title);  // Initialize Window and OpenGL Graphics
 #endif
 #endif
@@ -606,11 +630,13 @@ RLAPI void ToggleFullscreen(void);                                // Fullscreen
 RLAPI int GetScreenWidth(void);                                   // Get current screen width
 RLAPI int GetScreenWidth(void);                                   // Get current screen width
 RLAPI int GetScreenHeight(void);                                  // Get current screen height
 RLAPI int GetScreenHeight(void);                                  // Get current screen height
 
 
+#if !defined(PLATFORM_ANDROID)
 RLAPI void ShowCursor(void);                                      // Shows cursor
 RLAPI void ShowCursor(void);                                      // Shows cursor
 RLAPI void HideCursor(void);                                      // Hides cursor
 RLAPI void HideCursor(void);                                      // Hides cursor
 RLAPI bool IsCursorHidden(void);                                  // Returns true if cursor is not visible
 RLAPI bool IsCursorHidden(void);                                  // Returns true if cursor is not visible
 RLAPI void EnableCursor(void);                                    // Enables cursor
 RLAPI void EnableCursor(void);                                    // Enables cursor
 RLAPI void DisableCursor(void);                                   // Disables cursor
 RLAPI void DisableCursor(void);                                   // Disables cursor
+#endif
 
 
 RLAPI void ClearBackground(Color color);                          // Sets Background Color
 RLAPI void ClearBackground(Color color);                          // Sets Background Color
 RLAPI void BeginDrawing(void);                                    // Setup drawing canvas to start drawing
 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)
 // 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 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 IsKeyDown(int key);                                // Detect if a key is being pressed
 RLAPI bool IsKeyReleased(int key);                            // Detect if a key has been released once
 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 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 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 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 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 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
 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 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
 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 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)
 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)
 // 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 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 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 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 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 ImageFormat(Image *image, int newFormat);                                                     // Convert image data to desired format
 RLAPI void ImageAlphaMask(Image *image, Image alphaMask);                                                // Apply alpha mask to image
 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 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 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 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 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
 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 GetDefaultFont(void);                                                                   // Get the default SpriteFont
 RLAPI SpriteFont LoadSpriteFont(const char *fileName);                                                   // Load a SpriteFont image into GPU memory
 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 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)
 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 InitVrDevice(int vdDevice);            // Init VR device
 RLAPI void CloseVrDevice(void);                   // Close VR device
 RLAPI void CloseVrDevice(void);                   // Close VR device
 RLAPI bool IsVrDeviceReady(void);                 // Detect if VR device (or simulator) is ready
 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)
 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 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 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 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 UnloadSound(Sound sound);                                  // Unload sound
 RLAPI void PlaySound(Sound sound);                                    // Play a sound
 RLAPI void PlaySound(Sound sound);                                    // Play a sound
 RLAPI void PauseSound(Sound sound);                                   // Pause a sound
 RLAPI void PauseSound(Sound sound);                                   // Pause a sound

+ 91 - 24
src/rlgl.c

@@ -35,7 +35,7 @@
 #include <math.h>                   // Required for: atan2()
 #include <math.h>                   // Required for: atan2()
 
 
 #ifndef RLGL_STANDALONE
 #ifndef RLGL_STANDALONE
-    #include "raymath.h"            // Required for Vector3 and Matrix functions
+    #include "raymath.h"            // Required for: Vector3 and Matrix functions
 #endif
 #endif
 
 
 #if defined(GRAPHICS_API_OPENGL_11)
 #if defined(GRAPHICS_API_OPENGL_11)
@@ -140,6 +140,14 @@
     #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR     0x93b7
     #define GL_COMPRESSED_RGBA_ASTC_8x8_KHR     0x93b7
 #endif
 #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)
 #if defined(GRAPHICS_API_OPENGL_11)
     #define GL_UNSIGNED_SHORT_5_6_5     0x8363
     #define GL_UNSIGNED_SHORT_5_6_5     0x8363
     #define GL_UNSIGNED_SHORT_5_5_5_1   0x8034
     #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 Shader currentShader;                // Shader to be used on rendering (by default, defaultShader)
 static bool standardShaderLoaded = false;   // Flag to track if standard shader has been loaded
 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)
 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 texCompETC1Supported = false;   // ETC1 texture compression support
 static bool texCompETC2Supported = false;   // ETC2/EAC texture compression support
 static bool texCompETC2Supported = false;   // ETC2/EAC texture compression support
 static bool texCompPVRTSupported = false;   // PVR texture compression support
 static bool texCompPVRTSupported = false;   // PVR texture compression support
 static bool texCompASTCSupported = false;   // ASTC 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
 #endif
 
 
 #if defined(RLGL_OCULUS_SUPPORT)
 #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_OCULUS_SUPPORT)
 #if !defined(RLGL_STANDALONE)
 #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
 #endif
 
 
 static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height);    // Load Oculus required buffers
 static OculusBuffer LoadOculusBuffer(ovrSession session, int width, int height);    // Load Oculus required buffers
@@ -871,6 +886,37 @@ void rlDisableTexture(void)
 #endif
 #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)
 // Enable rendering to texture (fbo)
 void rlEnableRenderTexture(unsigned int id)
 void rlEnableRenderTexture(unsigned int id)
 {
 {
@@ -1124,7 +1170,7 @@ void rlglInit(int width, int height)
         // Check NPOT textures support
         // Check NPOT textures support
         // NOTE: Only check on OpenGL ES, OpenGL 3.3 has NPOT textures full support as core feature
         // 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;
         if (strcmp(extList[i], (const char *)"GL_OES_texture_npot") == 0) npotSupported = true;
-#endif   
+#endif
         
         
         // DDS texture compression support
         // DDS texture compression support
         if ((strcmp(extList[i], (const char *)"GL_EXT_texture_compression_s3tc") == 0) ||
         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
         // ASTC texture compression support
         if (strcmp(extList[i], (const char *)"GL_KHR_texture_compression_astc_hdr") == 0) texCompASTCSupported = true;
         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
 #ifdef _MSC_VER
@@ -1162,6 +1218,9 @@ void rlglInit(int width, int height)
     if (texCompETC2Supported) TraceLog(INFO, "[EXTENSION] ETC2/EAC compressed textures supported");
     if (texCompETC2Supported) TraceLog(INFO, "[EXTENSION] ETC2/EAC compressed textures supported");
     if (texCompPVRTSupported) TraceLog(INFO, "[EXTENSION] PVRT compressed textures supported");
     if (texCompPVRTSupported) TraceLog(INFO, "[EXTENSION] PVRT compressed textures supported");
     if (texCompASTCSupported) TraceLog(INFO, "[EXTENSION] ASTC 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
     // Initialize buffers, default shaders and default textures
     //----------------------------------------------------------
     //----------------------------------------------------------
@@ -1522,7 +1581,7 @@ RenderTexture2D rlglLoadRenderTexture(int width, int height)
     target.texture.id = 0;
     target.texture.id = 0;
     target.texture.width = width;
     target.texture.width = width;
     target.texture.height = height;
     target.texture.height = height;
-    target.texture.format = UNCOMPRESSED_R8G8B8;
+    target.texture.format = UNCOMPRESSED_R8G8B8A8;
     target.texture.mipmaps = 1;
     target.texture.mipmaps = 1;
     
     
     target.depth.id = 0;
     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_WRAP_T, GL_CLAMP_TO_EDGE);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
     glBindTexture(GL_TEXTURE_2D, 0);
     
     
 #if defined(GRAPHICS_API_OPENGL_33)
 #if defined(GRAPHICS_API_OPENGL_33)
@@ -1687,6 +1746,7 @@ void rlglGenerateMipmaps(Texture2D texture)
 #endif
 #endif
 
 
 #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)
 #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
         glGenerateMipmap(GL_TEXTURE_2D);    // Generate mipmaps automatically
         TraceLog(INFO, "[TEX ID %i] Mipmaps generated automatically", texture.id);
         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.hResolution = 2160;                 // HMD horizontal resolution in pixels
             hmd.vResolution = 1200;                 // HMD vertical resolution in pixels
             hmd.vResolution = 1200;                 // HMD vertical resolution in pixels
             hmd.hScreenSize = 0.133793f;            // HMD horizontal size in meters
             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.vScreenCenter = 0.04678f;           // HMD screen center in meters
             hmd.eyeToScreenDistance = 0.041f;       // HMD distance between eye and display in meters
             hmd.eyeToScreenDistance = 0.041f;       // HMD distance between eye and display in meters
             hmd.lensSeparationDistance = 0.07f;     // HMD lens separation distance in meters
             hmd.lensSeparationDistance = 0.07f;     // HMD lens separation distance in meters
@@ -2710,6 +2770,12 @@ bool IsVrDeviceReady(void)
     return (vrDeviceReady || vrSimulator) && vrEnabled;
     return (vrDeviceReady || vrSimulator) && vrEnabled;
 }
 }
 
 
+// Detect if VR simulator is running
+bool IsVrSimulator(void)
+{
+    return vrSimulator;
+}
+
 // Enable/Disable VR experience (device or simulator)
 // Enable/Disable VR experience (device or simulator)
 void ToggleVrMode(void)
 void ToggleVrMode(void)
 {
 {
@@ -2728,16 +2794,13 @@ void ToggleVrMode(void)
 #endif
 #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 defined(RLGL_OCULUS_SUPPORT)
-    if (vrDeviceReady) UpdateOculusTracking();
-    else
+    if (vrDeviceReady) UpdateOculusTracking(camera);
 #endif
 #endif
-    {
-        // TODO: Use alternative inputs (mouse, keyboard) to simulate tracking data (eyes position/orientation)
-    }
 }
 }
 
 
 // Begin Oculus drawing configuration
 // Begin Oculus drawing configuration
@@ -3779,8 +3842,8 @@ static void SetStereoConfig(VrDeviceInfo hmd)
 
 
     // Compute lens parameters
     // Compute lens parameters
     float lensShift = (hmd.hScreenSize*0.25f - hmd.lensSeparationDistance*0.5f)/hmd.hScreenSize;
     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 leftScreenCenter[2] = { 0.25f, 0.5f };
     float rightScreenCenter[2] = { 0.75f, 0.5f };
     float rightScreenCenter[2] = { 0.75f, 0.5f };
     
     
@@ -3797,8 +3860,8 @@ static void SetStereoConfig(VrDeviceInfo hmd)
     
     
     float normScreenWidth = 0.5f;
     float normScreenWidth = 0.5f;
     float normScreenHeight = 1.0f;
     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: LeftLensCenter = { %f, %f }", leftLensCenter[0], leftLensCenter[1]);
     TraceLog(DEBUG, "VR: Distortion Shader: RightLensCenter = { %f, %f }", rightLensCenter[0], rightLensCenter[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
 // Update Oculus head position-orientation tracking
-OCULUSAPI void UpdateOculusTracking(void)
+OCULUSAPI void UpdateOculusTracking(Camera *camera)
 {
 {
     frameIndex++;
     frameIndex++;
 
 
@@ -4086,6 +4149,10 @@ OCULUSAPI void UpdateOculusTracking(void)
     layer.eyeLayer.RenderPose[0] = eyePoses[0];
     layer.eyeLayer.RenderPose[0] = eyePoses[0];
     layer.eyeLayer.RenderPose[1] = eyePoses[1];
     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
     // Get session status information
     ovrSessionStatus sessionStatus;
     ovrSessionStatus sessionStatus;
     ovr_GetSessionStatus(session, &sessionStatus);
     ovr_GetSessionStatus(session, &sessionStatus);

+ 38 - 4
src/rlgl.h

@@ -90,15 +90,33 @@
     #define MAX_QUADS_BATCH         1024    // Be careful with text, every letter maps a quad
     #define MAX_QUADS_BATCH         1024    // Be careful with text, every letter maps a quad
 #endif
 #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
 // 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_PROJECTION, RL_MODELVIEW, RL_TEXTURE } MatrixMode;
 
 
 typedef enum { RL_LINES, RL_TRIANGLES, RL_QUADS } DrawMode;
 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)
 #if defined(RLGL_STANDALONE)
     #ifndef __cplusplus
     #ifndef __cplusplus
     // Boolean type
     // Boolean type
@@ -236,6 +254,21 @@ typedef enum { OPENGL_11 = 1, OPENGL_21, OPENGL_33, OPENGL_ES_20 } GlVersion;
 
 
     // Light types
     // Light types
     typedef enum { LIGHT_POINT, LIGHT_DIRECTIONAL, LIGHT_SPOT } LightType;
     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)
     // Color blending modes (pre-defined)
     typedef enum { BLEND_ALPHA = 0, BLEND_ADDITIVE, BLEND_MULTIPLIED } BlendMode;
     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 rlEnableTexture(unsigned int id);          // Enable texture usage
 void rlDisableTexture(void);                    // Disable 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 rlEnableRenderTexture(unsigned int id);    // Enable render texture (fbo)
 void rlDisableRenderTexture(void);              // Disable render texture (fbo), return to default framebuffer
 void rlDisableRenderTexture(void);              // Disable render texture (fbo), return to default framebuffer
 void rlEnableDepthTest(void);                   // Enable depth test
 void rlEnableDepthTest(void);                   // Enable depth test
@@ -375,13 +409,13 @@ float *MatrixToFloat(Matrix mat);
 void InitVrDevice(int vrDevice);            // Init VR device
 void InitVrDevice(int vrDevice);            // Init VR device
 void CloseVrDevice(void);                   // Close VR device
 void CloseVrDevice(void);                   // Close VR device
 bool IsVrDeviceReady(void);                 // Detect if VR device (or simulator) is ready
 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)
 void ToggleVrMode(void);                    // Enable/Disable VR experience (device or simulator)
 
 
 // Oculus Rift API for direct access the device (no simulator)
 // Oculus Rift API for direct access the device (no simulator)
 bool InitOculusDevice(void);                // Initialize Oculus device (returns true if success)
 bool InitOculusDevice(void);                // Initialize Oculus device (returns true if success)
 void CloseOculusDevice(void);               // Close Oculus device
 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 BeginOculusDrawing(void);              // Setup Oculus buffers for drawing
 void EndOculusDrawing(void);                // Finish Oculus drawing and blit framebuffer to mirror
 void EndOculusDrawing(void);                // Finish Oculus drawing and blit framebuffer to mirror
 #endif
 #endif

+ 83 - 0
src/rlua.h

@@ -2824,6 +2824,28 @@ int lua_IsAudioDeviceReady(lua_State* L)
     return 1;
     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)
 int lua_LoadSound(lua_State* L)
 {
 {
     const char * arg1 = LuaGetArgument_string(L, 1);
     const char * arg1 = LuaGetArgument_string(L, 1);
@@ -2849,6 +2871,22 @@ int lua_LoadSoundFromRES(lua_State* L)
     return 1;
     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)
 int lua_UnloadSound(lua_State* L)
 {
 {
     Sound arg1 = LuaGetArgument_Sound(L, 1);
     Sound arg1 = LuaGetArgument_Sound(L, 1);
@@ -2908,6 +2946,43 @@ int lua_SetSoundPitch(lua_State* L)
     return 0;
     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)
 int lua_LoadMusicStream(lua_State* L)
 {
 {
     const char * arg1 = LuaGetArgument_string(L, 1);
     const char * arg1 = LuaGetArgument_string(L, 1);
@@ -3799,9 +3874,13 @@ static luaL_Reg raylib_functions[] = {
     REG(InitAudioDevice)
     REG(InitAudioDevice)
     REG(CloseAudioDevice)
     REG(CloseAudioDevice)
     REG(IsAudioDeviceReady)
     REG(IsAudioDeviceReady)
+    REG(LoadWave)
+    REG(LoadWaveEx)
     REG(LoadSound)
     REG(LoadSound)
     REG(LoadSoundFromWave)
     REG(LoadSoundFromWave)
     REG(LoadSoundFromRES)
     REG(LoadSoundFromRES)
+    REG(UpdateSound)
+    REG(UnloadWave)
     REG(UnloadSound)
     REG(UnloadSound)
     REG(PlaySound)
     REG(PlaySound)
     REG(PauseSound)
     REG(PauseSound)
@@ -3810,6 +3889,10 @@ static luaL_Reg raylib_functions[] = {
     REG(IsSoundPlaying)
     REG(IsSoundPlaying)
     REG(SetSoundVolume)
     REG(SetSoundVolume)
     REG(SetSoundPitch)
     REG(SetSoundPitch)
+    REG(WaveFormat)
+    REG(WaveCopy)
+    REG(WaveCrop)
+    REG(GetWaveData)
 
 
     REG(LoadMusicStream)
     REG(LoadMusicStream)
     REG(UnloadMusicStream)
     REG(UnloadMusicStream)

+ 5 - 5
src/shader_standard.h

@@ -90,7 +90,7 @@ static const char fStandardShaderStr[] =
 "    if (diff > 0.0)\n"
 "    if (diff > 0.0)\n"
 "    {\n"
 "    {\n"
 "        vec3 h = normalize(-l.direction + v);\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"
 "    }\n"
 "    return (diff*l.diffuse.rgb + spec*colSpecular.rgb);\n"
 "    return (diff*l.diffuse.rgb + spec*colSpecular.rgb);\n"
 "}\n"
 "}\n"
@@ -103,7 +103,7 @@ static const char fStandardShaderStr[] =
 "    if (diff > 0.0)\n"
 "    if (diff > 0.0)\n"
 "    {\n"
 "    {\n"
 "        vec3 h = normalize(lightDir + v);\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"
 "    }\n"
 "    return (diff*l.intensity*l.diffuse.rgb + spec*colSpecular.rgb);\n"
 "    return (diff*l.intensity*l.diffuse.rgb + spec*colSpecular.rgb);\n"
 "}\n"
 "}\n"
@@ -124,7 +124,7 @@ static const char fStandardShaderStr[] =
 "    if (diffAttenuation > 0.0)\n"
 "    if (diffAttenuation > 0.0)\n"
 "    {\n"
 "    {\n"
 "        vec3 h = normalize(lightDir + v);\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"
 "    }\n"
 "    return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));\n"
 "    return (falloff*(diffAttenuation*l.diffuse.rgb + spec*colSpecular.rgb));\n"
 "}\n"
 "}\n"
@@ -152,9 +152,9 @@ static const char fStandardShaderStr[] =
 "    }\n"
 "    }\n"
 "    float spec = 1.0;\n"
 "    float spec = 1.0;\n"
 #if defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_21)
 #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)
 #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
 #endif
 "    for (int i = 0; i < maxLights; i++)\n"
 "    for (int i = 0; i < maxLights; i++)\n"
 "    {\n"
 "    {\n"

+ 21 - 22
src/shapes.c

@@ -4,7 +4,7 @@
 *
 *
 *   Basic functions to draw 2d Shapes and check collisions
 *   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
 *   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.
 *   will the authors be held liable for any damages arising from the use of this software.
@@ -25,9 +25,8 @@
 
 
 #include "raylib.h"
 #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
 #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);
     rlBegin(RL_LINES);
         rlColor4ub(color.r, color.g, color.b, color.a);
         rlColor4ub(color.r, color.g, color.b, color.a);
         rlVertex2f(position.x, position.y);
         rlVertex2f(position.x, position.y);
-        rlVertex2i(position.x + 1, position.y + 1);
+        rlVertex2f(position.x + 1.0f, position.y + 1.0f);
     rlEnd();
     rlEnd();
 }
 }
 
 
@@ -98,7 +97,7 @@ void DrawLineV(Vector2 startPos, Vector2 endPos, Color color)
 // Draw a color-filled circle
 // Draw a color-filled circle
 void DrawCircle(int centerX, int centerY, float radius, Color color)
 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
 // 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);
             rlColor4ub(color1.r, color1.g, color1.b, color1.a);
             rlVertex2i(centerX, centerY);
             rlVertex2i(centerX, centerY);
             rlColor4ub(color2.r, color2.g, color2.b, color2.a);
             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);
             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();
     rlEnd();
 }
 }
@@ -129,9 +128,9 @@ void DrawCircleV(Vector2 center, float radius, Color color)
             {
             {
                 rlColor4ub(color.r, color.g, color.b, color.a);
                 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();
         rlEnd();
     }
     }
@@ -144,10 +143,10 @@ void DrawCircleV(Vector2 center, float radius, Color color)
             {
             {
                 rlColor4ub(color.r, color.g, color.b, color.a);
                 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();
         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)
         // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360)
         for (int i = 0; i < 360; i += 10)
         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();
    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);
                 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();
         rlEnd();
     rlPopMatrix();
     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 dx = center2.x - center1.x;      // X distance between centers
     float dy = center2.y - center1.y;      // Y 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;
     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 (dx <= (rec.width/2)) { return true; } 
     if (dy <= (rec.height/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));
     return (cornerDistanceSq <= (radius*radius));
 }
 }

+ 145 - 67
src/text.c

@@ -4,7 +4,7 @@
 *
 *
 *   Basic functions to load SpriteFonts and draw Text
 *   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
 *   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.
 *   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 <stdarg.h>         // Required for: va_list, va_start(), vfprintf(), va_end()
 #include <stdio.h>          // Required for: FILE, fopen(), fclose(), fscanf(), feof(), rewind(), fgets()
 #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()
 // Following libs are used on LoadTTF()
+//#define STBTT_STATIC
 #define STB_TRUETYPE_IMPLEMENTATION
 #define STB_TRUETYPE_IMPLEMENTATION
 #include "external/stb_truetype.h"      // Required for: stbtt_BakeFontBitmap()
 #include "external/stb_truetype.h"      // Required for: stbtt_BakeFontBitmap()
 
 
@@ -43,7 +44,6 @@
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Defines and Macros
 // Defines and Macros
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
-#define FONT_FIRST_CHAR         32      // NOTE: Expected first char for a sprite font
 #define MAX_FORMATTEXT_LENGTH   64
 #define MAX_FORMATTEXT_LENGTH   64
 #define MAX_SUBTEXT_LENGTH      64
 #define MAX_SUBTEXT_LENGTH      64
 
 
@@ -68,12 +68,12 @@ static SpriteFont defaultFont;        // Default font provided by raylib
 //----------------------------------------------------------------------------------
 //----------------------------------------------------------------------------------
 // Module specific Functions Declaration
 // 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 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 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)
 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 LoadDefaultFont(void);
 extern void UnloadDefaultFont(void);
 extern void UnloadDefaultFont(void);
@@ -198,7 +198,7 @@ extern void LoadDefaultFont(void)
 
 
     for (int i = 0; i < defaultFont.numChars; i++)
     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].x = currentPosX;
         defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor);
         defaultFont.charRecs[i].y = charsDivisor + currentLine*(charsHeight + charsDivisor);
@@ -249,17 +249,18 @@ SpriteFont LoadSpriteFont(const char *fileName)
     // Default hardcoded values for ttf file loading
     // Default hardcoded values for ttf file loading
     #define DEFAULT_TTF_FONTSIZE    32      // Font first character (32 - space)
     #define DEFAULT_TTF_FONTSIZE    32      // Font first character (32 - space)
     #define DEFAULT_TTF_NUMCHARS    95      // ASCII 32..126 is 95 glyphs
     #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 };
     SpriteFont spriteFont = { 0 };
 
 
     // Check file extension
     // Check file extension
     if (strcmp(GetExtension(fileName),"rbmf") == 0) spriteFont = LoadRBMF(fileName);
     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 if (strcmp(GetExtension(fileName),"fnt") == 0) spriteFont = LoadBMFont(fileName);
     else
     else
     {
     {
         Image image = LoadImage(fileName);
         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);
         UnloadImage(image);
     }
     }
 
 
@@ -268,6 +269,38 @@ SpriteFont LoadSpriteFont(const char *fileName)
         TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName);
         TraceLog(WARNING, "[%s] SpriteFont could not be loaded, using default font", fileName);
         spriteFont = GetDefaultFont();
         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;
     return spriteFont;
 }
 }
@@ -293,13 +326,17 @@ void UnloadSpriteFont(SpriteFont spriteFont)
 // NOTE: chars spacing is proportional to fontSize
 // NOTE: chars spacing is proportional to fontSize
 void DrawText(const char *text, int posX, int posY, int fontSize, Color color)
 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
 // 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++)
     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!
         if ((unsigned char)text[i] == 0xc2)         // UTF-8 encoding identification HACK!
         {
         {
             // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿)
             // Support UTF-8 encoded values from [0xc2 0x80] -> [0xc2 0xbf](¿)
             letter = (unsigned char)text[i + 1];
             letter = (unsigned char)text[i + 1];
-            rec = spriteFont.charRecs[letter - FONT_FIRST_CHAR];
+            rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)letter)];
             i++;
             i++;
         }
         }
         else if ((unsigned char)text[i] == 0xc3)    // UTF-8 encoding identification HACK!
         else if ((unsigned char)text[i] == 0xc3)    // UTF-8 encoding identification HACK!
         {
         {
             // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ)
             // Support UTF-8 encoded values from [0xc3 0x80](À) -> [0xc3 0xbf](ÿ)
             letter = (unsigned char)text[i + 1];
             letter = (unsigned char)text[i + 1];
-            rec = spriteFont.charRecs[letter - FONT_FIRST_CHAR + 64];
+            rec = spriteFont.charRecs[GetCharIndex(spriteFont, (int)letter + 64)];
             i++;
             i++;
         }
         }
         else
         else
@@ -348,17 +381,19 @@ void DrawTextEx(SpriteFont spriteFont, const char *text, Vector2 position, float
                 textOffsetX = 0;
                 textOffsetX = 0;
                 rec.x = -1;
                 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);
                                                                  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
 // Measure string width for default font
 int MeasureText(const char *text, int fontSize)
 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;
     return (int)vec.x;
 }
 }
@@ -434,8 +473,10 @@ Vector2 MeasureTextEx(SpriteFont spriteFont, const char *text, int fontSize, int
 
 
         if (text[i] != '\n')
         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
         else
         {
         {
@@ -492,7 +533,28 @@ void DrawFPS(int posX, int posY)
 // Module specific Functions Definition
 // 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)
 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))
     #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 x = 0;
     int y = 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,
     // We allocate a temporal arrays for chars data measures,
     // once we get the actual number of chars, we copy data to a sized arrays
     // 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;
         xPosToRead = charSpacing;
     }
     }
 
 
-    free(pixels);
-
     TraceLog(DEBUG, "SpriteFont data parsed correctly from image");
     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
     // Create spritefont with all data parsed from image
     SpriteFont spriteFont = { 0 };
     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;
     spriteFont.numChars = index;
+    
+    UnloadImage(fontClear);     // Unload processed image once converted to texture
 
 
     // We got tempCharValues and tempCharsRecs populated with chars data
     // We got tempCharValues and tempCharsRecs populated with chars data
     // Now we move temp data to sized charValues and charRecs arrays
     // 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));
     strncat(texPath, texFileName, strlen(texFileName));
 
 
     TraceLog(DEBUG, "[%s] Font texture loading path: %s", fileName, texPath);
     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.size = fontSize;
     font.numChars = numChars;
     font.numChars = numChars;
     font.charValues = (int *)malloc(numChars*sizeof(int));
     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.charOffsets = (Vector2 *)malloc(numChars*sizeof(Vector2));
     font.charAdvanceX = (int *)malloc(numChars*sizeof(int));
     font.charAdvanceX = (int *)malloc(numChars*sizeof(int));
 
 
+    UnloadImage(imFont);
+    
     free(texPath);
     free(texPath);
 
 
     int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
     int charId, charX, charY, charWidth, charHeight, charOffsetX, charOffsetY, charAdvanceX;
 
 
-    bool unorderedChars = false;
-    int firstChar = 0;
-
     for (int i = 0; i < numChars; i++)
     for (int i = 0; i < numChars; i++)
     {
     {
         fgets(buffer, MAX_BUFFER_SIZE, fntFile);
         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",
         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);
                        &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
         // Save data properly in sprite font
         font.charValues[i] = charId;
         font.charValues[i] = charId;
         font.charRecs[i] = (Rectangle){ charX, charY, charWidth, charHeight };
         font.charRecs[i] = (Rectangle){ charX, charY, charWidth, charHeight };
@@ -837,11 +909,7 @@ static SpriteFont LoadBMFont(const char *fileName)
 
 
     fclose(fntFile);
     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);
         UnloadSpriteFont(font);
         font = GetDefaultFont();
         font = GetDefaultFont();
@@ -853,14 +921,17 @@ static SpriteFont LoadBMFont(const char *fileName)
 
 
 // Generate a sprite font from TTF file data (font size required)
 // Generate a sprite font from TTF file data (font size required)
 // TODO: Review texture packing method and generation (use oversampling)
 // 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 *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);
     stbtt_bakedchar *charData = (stbtt_bakedchar *)malloc(sizeof(stbtt_bakedchar)*numChars);
 
 
     SpriteFont font = { 0 };
     SpriteFont font = { 0 };
@@ -869,40 +940,47 @@ static SpriteFont LoadTTF(const char *fileName, int fontSize, int firstChar, int
 
 
     if (ttfFile == NULL)
     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;
         return font;
     }
     }
 
 
     fread(ttfBuffer, 1, 1<<25, ttfFile);
     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...
     // 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);
     free(ttfBuffer);
 
 
     // Convert image data from grayscale to to UNCOMPRESSED_GRAY_ALPHA
     // 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] = 255;
         dataGrayAlpha[k + 1] = dataBitmap[i];
         dataGrayAlpha[k + 1] = dataBitmap[i];
-
-        k += 2;
     }
     }
 
 
     free(dataBitmap);
     free(dataBitmap);
 
 
     // Sprite font generation from TTF extracted data
     // Sprite font generation from TTF extracted data
     Image image;
     Image image;
-    image.width = FONT_TEXTURE_WIDTH;
-    image.height = FONT_TEXTURE_HEIGHT;
+    image.width = textureSize;
+    image.height = textureSize;
     image.mipmaps = 1;
     image.mipmaps = 1;
     image.format = UNCOMPRESSED_GRAY_ALPHA;
     image.format = UNCOMPRESSED_GRAY_ALPHA;
     image.data = dataGrayAlpha;
     image.data = dataGrayAlpha;
-
+    
     font.texture = LoadTextureFromImage(image);
     font.texture = LoadTextureFromImage(image);
+    
+    //WritePNG("generated_ttf_image.png", (unsigned char *)image.data, image.width, image.height, 2);
+    
     UnloadImage(image);     // Unloads dataGrayAlpha
     UnloadImage(image);     // Unloads dataGrayAlpha
 
 
     font.size = fontSize;
     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++)
     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].x = (int)charData[i].x0;
         font.charRecs[i].y = (int)charData[i].y0;
         font.charRecs[i].y = (int)charData[i].y0;

+ 126 - 19
src/textures.c

@@ -8,7 +8,7 @@
 *       stb_image - Multiple formats image loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC)
 *       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
 *                   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
 *   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.
 *   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
 // TODO: Review function to support multiple color modes
 Image LoadImageFromRES(const char *rresName, int resId)
 Image LoadImageFromRES(const char *rresName, int resId)
 {
 {
-    Image image;
+    Image image = { 0 };
     bool found = false;
     bool found = false;
 
 
     char id[4];             // rRES file identifier
     char id[4];             // rRES file identifier
@@ -442,6 +442,96 @@ void UnloadRenderTexture(RenderTexture2D target)
     if (target.id != 0) rlDeleteRenderTextures(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
 // Get pixel data from image in the form of Color struct array
 Color *GetImageData(Image image)
 Color *GetImageData(Image image)
 {
 {
@@ -694,28 +784,45 @@ void ImageFormat(Image *image, int newFormat)
 }
 }
 
 
 // Apply alpha mask to image
 // 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
 // NOTE 2: alphaMask should be same size as image
 void ImageAlphaMask(Image *image, Image alphaMask)
 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");
         TraceLog(WARNING, "Alpha mask can not be applied to compressed data formats");
-        return;
     }
     }
     else
     else
     {
     {
         // Force mask to be Grayscale
         // Force mask to be Grayscale
         Image mask = ImageCopy(alphaMask);
         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);
         UnloadImage(mask);
@@ -1118,7 +1225,7 @@ Image ImageText(const char *text, int fontSize, Color color)
     if (fontSize < defaultFontSize) fontSize = defaultFontSize;
     if (fontSize < defaultFontSize) fontSize = defaultFontSize;
     int spacing = 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;
     return imText;
 }
 }
@@ -1134,7 +1241,7 @@ Image ImageTextEx(SpriteFont font, const char *text, float fontSize, int spacing
     // NOTE: GetTextureData() not available in OpenGL ES
     // NOTE: GetTextureData() not available in OpenGL ES
     Image imFont = GetTextureData(font.texture);
     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
     ImageColorTint(&imFont, tint);                  // Apply color tint to font
 
 
     Color *fontPixels = GetImageData(imFont);
     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)
 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)
     // 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)
 // 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;
     if (contrast > 100) contrast = 100;
     if (contrast > 100) contrast = 100;
 
 
-    contrast = (100.0 + contrast)/100.0;
+    contrast = (100.0f + contrast)/100.0f;
     contrast *= contrast;
     contrast *= contrast;
 
 
     Color *pixels = GetImageData(*image);
     Color *pixels = GetImageData(*image);
@@ -1326,7 +1433,7 @@ void ImageColorContrast(Image *image, float contrast)
     {
     {
         for (int x = 0; x < image->width; x++)
         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 -= 0.5;
             pR *= contrast;
             pR *= contrast;
             pR += 0.5;
             pR += 0.5;
@@ -1334,7 +1441,7 @@ void ImageColorContrast(Image *image, float contrast)
             if (pR < 0) pR = 0;
             if (pR < 0) pR = 0;
             if (pR > 255) pR = 255;
             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 -= 0.5;
             pG *= contrast;
             pG *= contrast;
             pG += 0.5;
             pG += 0.5;
@@ -1342,7 +1449,7 @@ void ImageColorContrast(Image *image, float contrast)
             if (pG < 0) pG = 0;
             if (pG < 0) pG = 0;
             if (pG > 255) pG = 255;
             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 -= 0.5;
             pB *= contrast;
             pB *= contrast;
             pB += 0.5;
             pB += 0.5;

+ 1 - 1
src/utils.c

@@ -8,7 +8,7 @@
 *       tinfl - zlib DEFLATE algorithm decompression lib
 *       tinfl - zlib DEFLATE algorithm decompression lib
 *       stb_image_write - PNG writting functions
 *       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
 *   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.
 *   will the authors be held liable for any damages arising from the use of this software.