summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTING.md7
-rw-r--r--examples/others/iqm_loader/riqm.h117
-rw-r--r--examples/physac/physics_demo.c6
-rw-r--r--examples/physac/physics_friction.c4
-rw-r--r--examples/physac/physics_movement.c4
-rw-r--r--examples/physac/physics_restitution.c4
-rw-r--r--examples/physac/physics_shatter.c7
-rw-r--r--examples/textures/textures_image_processing.c6
-rw-r--r--examples/textures/textures_image_text.c6
-rw-r--r--examples/textures/textures_particles_blending.c6
-rw-r--r--examples/textures/textures_srcrec_dstrec.c12
-rw-r--r--games/koala_seasons/screens/screen_title.c4
-rw-r--r--release/include/raylib.h8
-rw-r--r--release/libs/win32/mingw32/libraylib.abin1237446 -> 1239504 bytes
-rw-r--r--src/camera.h4
-rw-r--r--src/core.c4
-rw-r--r--src/external/mini_al.c6
-rw-r--r--src/external/mini_al.h3760
-rw-r--r--src/models.c10
-rw-r--r--src/physac.h64
-rw-r--r--src/raymath.h40
-rw-r--r--src/rlgl.h16
-rw-r--r--src/shapes.c46
-rw-r--r--src/text.c44
-rw-r--r--src/textures.c67
25 files changed, 3032 insertions, 1220 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bd6ae03b..cbdd7692 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -65,9 +65,14 @@ Some people ported raylib to other languages in form of bindings or wrappers to
- [raylib Nim binding](https://gitlab.com/define-private-public/raylib-Nim)
- [raylib Crystal binding](https://gitlab.com/Zatherz/cray)
- [raylib Perl wrapper](https://metacpan.org/pod/Graphics::Raylib)
- - raylib Pascal binding with custom IDE (closed-source)
+ - [raylib Pascal binding](https://github.com/drezgames/raylib-pascal)
- [raylib flat-assembler Usage example](http://forum.raylib.com/index.php?p=/discussion/comment/425/#Comment_425)
- [raylib COBOL Usage example](https://github.com/Martinfx/Cobol/tree/master/OpenCobol/Games/raylib)
+ - [raylib C# binding](https://github.com/ChrisDill/Raylib-cs)
+ - [raylib C# binding](https://github.com/TheLumaio/RaylibSharp)
+ - [raylib Ruby binding](https://github.com/D3nX/raylib-ruby-ffi)
+ - [raylib Rust binding](https://github.com/deltaphc/raylib-rs)
+ - [raylib Python binding](https://github.com/overdev/raylibpy)
Usually, raylib bindings follow the convention: `raylib-{language}`
diff --git a/examples/others/iqm_loader/riqm.h b/examples/others/iqm_loader/riqm.h
index 24ed3686..694e3a95 100644
--- a/examples/others/iqm_loader/riqm.h
+++ b/examples/others/iqm_loader/riqm.h
@@ -64,29 +64,32 @@ typedef struct Pose {
} Pose;
typedef struct Animation {
- int jointCount;
- Joint *joints; // NOTE: Joints in anims do not have names
+ int jointCount; // Number of joints (bones)
+ Joint *joints; // Joints array
+ // NOTE: Joints in anims do not have names
- int frameCount;
- float framerate;
+ int frameCount; // Number of animation frames
+ float framerate; // Frame change speed
- Pose **framepose;
+ Pose **framepose; // Poses array by frame (and one pose by joint)
} Animation;
// Animated Model type
typedef struct AnimatedModel {
- int meshCount;
- Mesh *mesh;
-
- int materialCount;
- int *meshMaterialId;
- Material *materials;
+ Matrix transform; // Local transform matrix
+
+ int meshCount; // Number of meshes
+ Mesh *meshes; // Meshes array
- int jointCount;
- Joint *joints;
+ int materialCount; // Number of materials
+ Material *materials; // Materials array
+
+ int *meshMaterialId; // Mesh materials ids
- Pose *basepose;
- Matrix transform;
+ // Animation required data
+ int jointCount; // Number of joints (and keyposes)
+ Joint *joints; // Mesh joints (bones)
+ Pose *basepose; // Mesh base-poses by joint
} AnimatedModel;
//----------------------------------------------------------------------------------
@@ -233,7 +236,7 @@ AnimatedModel LoadAnimatedModel(const char *filename)
{
AnimatedModel out = LoadIQM(filename);
- for (int i = 0; i < out.meshCount; i++) rlLoadMesh(&out.mesh[i], false);
+ for (int i = 0; i < out.meshCount; i++) rlLoadMesh(&out.meshes[i], false);
out.transform = MatrixIdentity();
out.meshMaterialId = malloc(sizeof(int)*out.meshCount);
@@ -258,7 +261,7 @@ AnimatedModel AnimatedModelAddTexture(AnimatedModel model, const char *filename)
return model;
}
-// Set the material for a mesh
+// Set the material for a meshes
AnimatedModel SetMeshMaterial(AnimatedModel model, int meshid, int textureid)
{
if (meshid > model.meshCount)
@@ -466,9 +469,9 @@ void UnloadAnimatedModel(AnimatedModel model)
free(model.joints);
free(model.basepose);
- for (int i = 0; i < model.meshCount; i++) rlUnloadMesh(&model.mesh[i]);
+ for (int i = 0; i < model.meshCount; i++) rlUnloadMesh(&model.meshes[i]);
- free(model.mesh);
+ free(model.meshes);
}
// Unload animation
@@ -515,9 +518,9 @@ void AnimateModel(AnimatedModel model, Animation anim, int frame)
int wcounter = 0;
int weightId = 0;
- for (int i = 0; i < model.mesh[m].vertexCount; i++)
+ for (int i = 0; i < model.meshes[m].vertexCount; i++)
{
- weightId = model.mesh[m].weightId[wcounter];
+ weightId = model.meshes[m].weightId[wcounter];
baset = model.basepose[weightId].translation;
baser = model.basepose[weightId].rotation;
bases = model.basepose[weightId].scale;
@@ -526,23 +529,23 @@ void AnimateModel(AnimatedModel model, Animation anim, int frame)
outs = anim.framepose[frame][weightId].scale;
// vertices
- // NOTE: We use mesh.baseVertices (default position) to calculate mesh.vertices (animated position)
- outv = (Vector3){ model.mesh[m].baseVertices[vcounter], model.mesh[m].baseVertices[vcounter + 1], model.mesh[m].baseVertices[vcounter + 2] };
+ // NOTE: We use meshes.baseVertices (default position) to calculate meshes.vertices (animated position)
+ outv = (Vector3){ model.meshes[m].baseVertices[vcounter], model.meshes[m].baseVertices[vcounter + 1], model.meshes[m].baseVertices[vcounter + 2] };
outv = Vector3MultiplyV(outv, outs);
outv = Vector3Subtract(outv, baset);
outv = Vector3RotateByQuaternion(outv, QuaternionMultiply(outr, QuaternionInvert(baser)));
outv = Vector3Add(outv, outt);
- model.mesh[m].vertices[vcounter] = outv.x;
- model.mesh[m].vertices[vcounter + 1] = outv.y;
- model.mesh[m].vertices[vcounter + 2] = outv.z;
+ model.meshes[m].vertices[vcounter] = outv.x;
+ model.meshes[m].vertices[vcounter + 1] = outv.y;
+ model.meshes[m].vertices[vcounter + 2] = outv.z;
// normals
- // NOTE: We use mesh.baseNormals (default normal) to calculate mesh.normals (animated normals)
- outn = (Vector3){ model.mesh[m].baseNormals[vcounter], model.mesh[m].baseNormals[vcounter + 1], model.mesh[m].baseNormals[vcounter + 2] };
+ // NOTE: We use meshes.baseNormals (default normal) to calculate meshes.normals (animated normals)
+ outn = (Vector3){ model.meshes[m].baseNormals[vcounter], model.meshes[m].baseNormals[vcounter + 1], model.meshes[m].baseNormals[vcounter + 2] };
outn = Vector3RotateByQuaternion(outn, QuaternionMultiply(outr, QuaternionInvert(baser)));
- model.mesh[m].normals[vcounter] = outn.x;
- model.mesh[m].normals[vcounter + 1] = outn.y;
- model.mesh[m].normals[vcounter + 2] = outn.z;
+ model.meshes[m].normals[vcounter] = outn.x;
+ model.meshes[m].normals[vcounter + 1] = outn.y;
+ model.meshes[m].normals[vcounter + 2] = outn.z;
vcounter += 3;
wcounter += 4;
}
@@ -563,7 +566,7 @@ void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotation
{
if (model.materialCount == 0)
{
- TraceLog(LOG_WARNING,"No materials set, can't draw animated mesh\n");
+ TraceLog(LOG_WARNING,"No materials set, can't draw animated meshes\n");
return;
}
@@ -576,9 +579,9 @@ void DrawAnimatedModelEx(AnimatedModel model, Vector3 position, Vector3 rotation
for (int i = 0; i < model.meshCount; i++)
{
- rlUpdateMesh(model.mesh[i], 0, model.mesh[i].vertexCount); // Update vertex position
- rlUpdateMesh(model.mesh[i], 2, model.mesh[i].vertexCount); // Update vertex normals
- rlDrawMesh(model.mesh[i], model.materials[model.meshMaterialId[i]], model.transform); // Draw mesh
+ rlUpdateMesh(model.meshes[i], 0, model.meshes[i].vertexCount); // Update vertex position
+ rlUpdateMesh(model.meshes[i], 2, model.meshes[i].vertexCount); // Update vertex normals
+ rlDrawMesh(model.meshes[i], model.materials[model.meshMaterialId[i]], model.transform); // Draw meshes
}
}
@@ -632,7 +635,7 @@ static AnimatedModel LoadIQM(const char *filename)
fread(imesh, sizeof(IQMMesh)*iqm.num_meshes, 1, iqmFile);
model.meshCount = iqm.num_meshes;
- model.mesh = malloc(sizeof(Mesh)*iqm.num_meshes);
+ model.meshes = malloc(sizeof(Mesh)*iqm.num_meshes);
char name[MESH_NAME_LENGTH];
@@ -640,21 +643,21 @@ static AnimatedModel LoadIQM(const char *filename)
{
fseek(iqmFile,iqm.ofs_text+imesh[i].name,SEEK_SET);
fread(name, sizeof(char)*MESH_NAME_LENGTH, 1, iqmFile); // Mesh name not used...
- model.mesh[i].vertexCount = imesh[i].num_vertexes;
+ model.meshes[i].vertexCount = imesh[i].num_vertexes;
- model.mesh[i].baseVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base position
- model.mesh[i].baseNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base normal
+ model.meshes[i].baseVertices = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base position
+ model.meshes[i].baseNormals = malloc(sizeof(float)*imesh[i].num_vertexes*3); // Default IQM base normal
- model.mesh[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2);
- model.mesh[i].weightId = malloc(sizeof(int)*imesh[i].num_vertexes*4);
- model.mesh[i].weightBias = malloc(sizeof(float)*imesh[i].num_vertexes*4);
+ model.meshes[i].texcoords = malloc(sizeof(float)*imesh[i].num_vertexes*2);
+ model.meshes[i].weightId = malloc(sizeof(int)*imesh[i].num_vertexes*4);
+ model.meshes[i].weightBias = malloc(sizeof(float)*imesh[i].num_vertexes*4);
- model.mesh[i].triangleCount = imesh[i].num_triangles;
- model.mesh[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3);
+ model.meshes[i].triangleCount = imesh[i].num_triangles;
+ model.meshes[i].indices = malloc(sizeof(unsigned short)*imesh[i].num_triangles*3);
- // What we actually process for rendering, should be updated transforming mesh.vertices and mesh.normals
- model.mesh[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3);
- model.mesh[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3);
+ // What we actually process for rendering, should be updated transforming meshes.vertices and meshes.normals
+ model.meshes[i].vertices = malloc(sizeof(float)*imesh[i].num_vertexes*3);
+ model.meshes[i].normals = malloc(sizeof(float)*imesh[i].num_vertexes*3);
}
// tris
@@ -669,9 +672,9 @@ static AnimatedModel LoadIQM(const char *filename)
for (int i = imesh[m].first_triangle; i < imesh[m].first_triangle+imesh[m].num_triangles; i++)
{
// IQM triangles are stored counter clockwise, but raylib sets opengl to clockwise drawing, so we swap them around
- model.mesh[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex;
- model.mesh[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex;
- model.mesh[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex;
+ model.meshes[m].indices[tcounter+2] = tri[i].vertex[0] - imesh[m].first_vertex;
+ model.meshes[m].indices[tcounter+1] = tri[i].vertex[1] - imesh[m].first_vertex;
+ model.meshes[m].indices[tcounter] = tri[i].vertex[2] - imesh[m].first_vertex;
tcounter += 3;
}
}
@@ -696,8 +699,8 @@ static AnimatedModel LoadIQM(const char *filename)
int vcounter = 0;
for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
{
- model.mesh[m].vertices[vcounter] = vertex[i];
- model.mesh[m].baseVertices[vcounter] = vertex[i];
+ model.meshes[m].vertices[vcounter] = vertex[i];
+ model.meshes[m].baseVertices[vcounter] = vertex[i];
vcounter++;
}
}
@@ -713,8 +716,8 @@ static AnimatedModel LoadIQM(const char *filename)
int vcounter = 0;
for (int i = imesh[m].first_vertex*3; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*3; i++)
{
- model.mesh[m].normals[vcounter] = normal[i];
- model.mesh[m].baseNormals[vcounter] = normal[i];
+ model.meshes[m].normals[vcounter] = normal[i];
+ model.meshes[m].baseNormals[vcounter] = normal[i];
vcounter++;
}
}
@@ -730,7 +733,7 @@ static AnimatedModel LoadIQM(const char *filename)
int vcounter = 0;
for (int i = imesh[m].first_vertex*2; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*2; i++)
{
- model.mesh[m].texcoords[vcounter] = text[i];
+ model.meshes[m].texcoords[vcounter] = text[i];
vcounter++;
}
}
@@ -746,7 +749,7 @@ static AnimatedModel LoadIQM(const char *filename)
int vcounter = 0;
for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
{
- model.mesh[m].weightId[vcounter] = blendi[i];
+ model.meshes[m].weightId[vcounter] = blendi[i];
vcounter++;
}
}
@@ -762,7 +765,7 @@ static AnimatedModel LoadIQM(const char *filename)
int vcounter = 0;
for (int i = imesh[m].first_vertex*4; i < (imesh[m].first_vertex + imesh[m].num_vertexes)*4; i++)
{
- model.mesh[m].weightBias[vcounter] = blendw[i]/255.0f;
+ model.meshes[m].weightBias[vcounter] = blendw[i]/255.0f;
vcounter++;
}
}
diff --git a/examples/physac/physics_demo.c b/examples/physac/physics_demo.c
index 273b9931..d417efec 100644
--- a/examples/physac/physics_demo.c
+++ b/examples/physac/physics_demo.c
@@ -17,6 +17,7 @@
#include "raylib.h"
#define PHYSAC_IMPLEMENTATION
+#define PHYSAC_NO_THREADS
#include "physac.h"
int main()
@@ -54,6 +55,8 @@ int main()
// Update
//----------------------------------------------------------------------------------
// Delay initialization of variables due to physics reset async
+ RunPhysicsStep();
+
if (needsReset)
{
floor = CreatePhysicsBodyRectangle((Vector2){ screenWidth/2, screenHeight }, 500, 100, 10);
@@ -61,6 +64,8 @@ int main()
circle = CreatePhysicsBodyCircle((Vector2){ screenWidth/2, screenHeight/2 }, 45, 10);
circle->enabled = false;
+
+ needsReset = false;
}
// Reset physics input
@@ -134,4 +139,3 @@ int main()
return 0;
}
-
diff --git a/examples/physac/physics_friction.c b/examples/physac/physics_friction.c
index b4cc571d..99491eeb 100644
--- a/examples/physac/physics_friction.c
+++ b/examples/physac/physics_friction.c
@@ -17,6 +17,7 @@
#include "raylib.h"
#define PHYSAC_IMPLEMENTATION
+#define PHYSAC_NO_THREADS
#include "physac.h"
int main()
@@ -71,6 +72,8 @@ int main()
{
// Update
//----------------------------------------------------------------------------------
+ RunPhysicsStep();
+
if (IsKeyPressed('R')) // Reset physics input
{
// Reset dynamic physics bodies position, velocity and rotation
@@ -141,4 +144,3 @@ int main()
return 0;
}
-
diff --git a/examples/physac/physics_movement.c b/examples/physac/physics_movement.c
index 3ca69671..4c4f259f 100644
--- a/examples/physac/physics_movement.c
+++ b/examples/physac/physics_movement.c
@@ -17,6 +17,7 @@
#include "raylib.h"
#define PHYSAC_IMPLEMENTATION
+#define PHYSAC_NO_THREADS
#include "physac.h"
#define VELOCITY 0.5f
@@ -64,6 +65,8 @@ int main()
{
// Update
//----------------------------------------------------------------------------------
+ RunPhysicsStep();
+
if (IsKeyPressed('R')) // Reset physics input
{
// Reset movement physics body position, velocity and rotation
@@ -127,4 +130,3 @@ int main()
return 0;
}
-
diff --git a/examples/physac/physics_restitution.c b/examples/physac/physics_restitution.c
index 8e26c93f..d2ec49db 100644
--- a/examples/physac/physics_restitution.c
+++ b/examples/physac/physics_restitution.c
@@ -17,6 +17,7 @@
#include "raylib.h"
#define PHYSAC_IMPLEMENTATION
+#define PHYSAC_NO_THREADS
#include "physac.h"
int main()
@@ -57,6 +58,8 @@ int main()
{
// Update
//----------------------------------------------------------------------------------
+ RunPhysicsStep();
+
if (IsKeyPressed('R')) // Reset physics input
{
// Reset circles physics bodies position and velocity
@@ -120,4 +123,3 @@ int main()
return 0;
}
-
diff --git a/examples/physac/physics_shatter.c b/examples/physac/physics_shatter.c
index e34d6cec..17f9bfc2 100644
--- a/examples/physac/physics_shatter.c
+++ b/examples/physac/physics_shatter.c
@@ -17,7 +17,8 @@
#include "raylib.h"
#define PHYSAC_IMPLEMENTATION
-#include "physac.h"
+#define PHYSAC_NO_THREADS
+#include "physac.h"
int main()
{
@@ -48,12 +49,15 @@ int main()
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
+ RunPhysicsStep();
+
//----------------------------------------------------------------------------------
// Delay initialization of variables due to physics reset asynchronous
if (needsReset)
{
// Create random polygon physics body to shatter
CreatePhysicsBodyPolygon((Vector2){ screenWidth/2, screenHeight/2 }, GetRandomValue(80, 200), GetRandomValue(3, 8), 10);
+ needsReset = false;
}
if (IsKeyPressed('R')) // Reset physics input
@@ -118,4 +122,3 @@ int main()
return 0;
}
-
diff --git a/examples/textures/textures_image_processing.c b/examples/textures/textures_image_processing.c
index 427faa60..6d33d95b 100644
--- a/examples/textures/textures_image_processing.c
+++ b/examples/textures/textures_image_processing.c
@@ -59,7 +59,7 @@ int main()
Rectangle selectRecs[NUM_PROCESSES];
- for (int i = 0; i < NUM_PROCESSES; i++) selectRecs[i] = (Rectangle){ 40, 50 + 32*i, 150, 30 };
+ for (int i = 0; i < NUM_PROCESSES; i++) selectRecs[i] = (Rectangle){ 40.0f, (float)(50 + 32*i), 150.0f, 30.0f };
SetTargetFPS(60);
//---------------------------------------------------------------------------------------
@@ -122,8 +122,8 @@ int main()
for (int i = 0; i < NUM_PROCESSES; i++)
{
DrawRectangleRec(selectRecs[i], (i == currentProcess) ? SKYBLUE : LIGHTGRAY);
- DrawRectangleLines(selectRecs[i].x, selectRecs[i].y, selectRecs[i].width, selectRecs[i].height, (i == currentProcess) ? BLUE : GRAY);
- DrawText(processText[i], selectRecs[i].x + selectRecs[i].width/2 - MeasureText(processText[i], 10)/2, selectRecs[i].y + 11, 10, (i == currentProcess) ? DARKBLUE : DARKGRAY);
+ DrawRectangleLines((int)selectRecs[i].x, (int) selectRecs[i].y, (int) selectRecs[i].width, (int) selectRecs[i].height, (i == currentProcess) ? BLUE : GRAY);
+ DrawText( processText[i], (int)( selectRecs[i].x + selectRecs[i].width/2 - MeasureText(processText[i], 10)/2), (int) selectRecs[i].y + 11, 10, (i == currentProcess) ? DARKBLUE : DARKGRAY);
}
DrawTexture(texture, screenWidth - texture.width - 60, screenHeight/2 - texture.height/2, WHITE);
diff --git a/examples/textures/textures_image_text.c b/examples/textures/textures_image_text.c
index 78d25c14..c69f0f55 100644
--- a/examples/textures/textures_image_text.c
+++ b/examples/textures/textures_image_text.c
@@ -26,12 +26,12 @@ int main()
Image parrots = LoadImage("resources/parrots.png"); // Load image in CPU memory (RAM)
// Draw over image using custom font
- ImageDrawTextEx(&parrots, (Vector2){ 20, 20 }, font, "[Parrots font drawing]", font.baseSize, 0, WHITE);
+ ImageDrawTextEx(&parrots, (Vector2){ 20.0f, 20.0f }, font, "[Parrots font drawing]", (float)font.baseSize, 0.0f, WHITE);
Texture2D texture = LoadTextureFromImage(parrots); // Image converted to texture, uploaded to GPU memory (VRAM)
UnloadImage(parrots); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM
- Vector2 position = { screenWidth/2 - texture.width/2, screenHeight/2 - texture.height/2 - 20 };
+ Vector2 position = { (float)(screenWidth/2 - texture.width/2), (float)(screenHeight/2 - texture.height/2 - 20) };
bool showFont = false;
@@ -60,7 +60,7 @@ int main()
// Draw text directly using sprite font
DrawTextEx(font, "[Parrots font drawing]", (Vector2){ position.x + 20,
- position.y + 20 + 280 }, font.baseSize, 0, WHITE);
+ position.y + 20 + 280 }, (float)font.baseSize, 0.0f, WHITE);
}
else DrawTexture(font.texture, screenWidth/2 - font.texture.width/2, 50, BLACK);
diff --git a/examples/textures/textures_particles_blending.c b/examples/textures/textures_particles_blending.c
index 842ac77d..3b7dcaa3 100644
--- a/examples/textures/textures_particles_blending.c
+++ b/examples/textures/textures_particles_blending.c
@@ -42,7 +42,7 @@ int main()
mouseTail[i].color = (Color){ GetRandomValue(0, 255), GetRandomValue(0, 255), GetRandomValue(0, 255), 255 };
mouseTail[i].alpha = 1.0f;
mouseTail[i].size = (float)GetRandomValue(1, 30)/20.0f;
- mouseTail[i].rotation = GetRandomValue(0, 360);
+ mouseTail[i].rotation = (float)GetRandomValue(0, 360);
mouseTail[i].active = false;
}
@@ -107,9 +107,9 @@ int main()
// Draw active particles
for (int i = 0; i < MAX_PARTICLES; i++)
{
- if (mouseTail[i].active) DrawTexturePro(smoke, (Rectangle){ 0, 0, smoke.width, smoke.height },
+ if (mouseTail[i].active) DrawTexturePro(smoke, (Rectangle){ 0.0f, 0.0f, (float)smoke.width, (float)smoke.height },
(Rectangle){ mouseTail[i].position.x, mouseTail[i].position.y, smoke.width*mouseTail[i].size, smoke.height*mouseTail[i].size },
- (Vector2){ smoke.width*mouseTail[i].size/2, smoke.height*mouseTail[i].size/2 }, mouseTail[i].rotation,
+ (Vector2){ (float)(smoke.width*mouseTail[i].size/2.0f), (float)(smoke.height*mouseTail[i].size/2.0f) }, mouseTail[i].rotation,
Fade(mouseTail[i].color, mouseTail[i].alpha));
}
diff --git a/examples/textures/textures_srcrec_dstrec.c b/examples/textures/textures_srcrec_dstrec.c
index 53ffd1d0..cc08eb58 100644
--- a/examples/textures/textures_srcrec_dstrec.c
+++ b/examples/textures/textures_srcrec_dstrec.c
@@ -27,13 +27,13 @@ int main()
int frameHeight = scarfy.height;
// NOTE: Source rectangle (part of the texture to use for drawing)
- Rectangle sourceRec = { 0, 0, frameWidth, frameHeight };
+ Rectangle sourceRec = { 0.0f, 0.0f, (float)frameWidth, (float)frameHeight };
// NOTE: Destination rectangle (screen rectangle where drawing part of texture)
- Rectangle destRec = { screenWidth/2, screenHeight/2, frameWidth*2, frameHeight*2 };
+ Rectangle destRec = { (float)screenWidth/2, (float)screenHeight/2, (float)frameWidth*2, (float)frameHeight*2 };
// NOTE: Origin of the texture (rotation/scale point), it's relative to destination rectangle size
- Vector2 origin = { frameWidth, frameHeight };
+ Vector2 origin = { (float)frameWidth, (float)frameHeight };
int rotation = 0;
@@ -59,10 +59,10 @@ int main()
// destRec defines the rectangle where our texture part will fit (scaling it to fit)
// origin defines the point of the texture used as reference for rotation and scaling
// rotation defines the texture rotation (using origin as rotation point)
- DrawTexturePro(scarfy, sourceRec, destRec, origin, rotation, WHITE);
+ DrawTexturePro(scarfy, sourceRec, destRec, origin, (float)rotation, WHITE);
- DrawLine(destRec.x, 0, destRec.x, screenHeight, GRAY);
- DrawLine(0, destRec.y, screenWidth, destRec.y, GRAY);
+ DrawLine((int) destRec.x, 0, (int) destRec.x, screenHeight, GRAY);
+ DrawLine(0, (int)destRec.y, screenWidth, (int)destRec.y, GRAY);
DrawText("(c) Scarfy sprite by Eiden Marsal", screenWidth - 200, screenHeight - 20, 10, GRAY);
diff --git a/games/koala_seasons/screens/screen_title.c b/games/koala_seasons/screens/screen_title.c
index 0459906d..21a4df51 100644
--- a/games/koala_seasons/screens/screen_title.c
+++ b/games/koala_seasons/screens/screen_title.c
@@ -337,8 +337,8 @@ void UpdateTitleScreen(void)
if (framesCounter < duration)
{
- currentValue1 = BounceEaseOut((float) framesCounter, initValue1, (finishValue1 - initValue1), duration);
- currentValue2 = BounceEaseOut((float) framesCounter, initValue2, (finishValue2 - initValue2), duration);
+ currentValue1 = BounceEaseOut((float)framesCounter, initValue1, (finishValue1 - initValue1), duration);
+ currentValue2 = BounceEaseOut((float)framesCounter, initValue2, (finishValue2 - initValue2), duration);
}
thisFrame += 1*TIME_FACTOR;
diff --git a/release/include/raylib.h b/release/include/raylib.h
index a8e399da..8d1389f7 100644
--- a/release/include/raylib.h
+++ b/release/include/raylib.h
@@ -71,6 +71,8 @@
#ifndef RAYLIB_H
#define RAYLIB_H
+#include <stdarg.h> // Required for: va_list - Only used by TraceLogCallback
+
#if defined(_WIN32) && defined(BUILD_LIBTYPE_SHARED)
#define RLAPI __declspec(dllexport) // We are building raylib as a Win32 shared library (.dll)
#elif defined(_WIN32) && defined(USE_LIBTYPE_SHARED)
@@ -727,6 +729,9 @@ typedef enum {
HMD_SONY_PSVR
} VrDeviceType;
+// Callbacks to be implemented by users
+typedef void (*TraceLogCallback)(int msgType, const char *text, va_list args);
+
#ifdef __cplusplus
extern "C" { // Prevents name mangling of functions
#endif
@@ -779,7 +784,7 @@ RLAPI Ray GetMouseRay(Vector2 mousePosition, Camera camera); // Returns a r
RLAPI Vector2 GetWorldToScreen(Vector3 position, Camera camera); // Returns the screen space position for a 3d world space position
RLAPI Matrix GetCameraMatrix(Camera camera); // Returns camera transform matrix (view matrix)
-// Timing-related functions
+// timing-related functions
RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum)
RLAPI int GetFPS(void); // Returns current FPS
RLAPI float GetFrameTime(void); // Returns time in seconds for last frame drawn
@@ -796,6 +801,7 @@ RLAPI Color Fade(Color color, float alpha); // Color fade-
RLAPI void ShowLogo(void); // Activate raylib logo at startup (can be done with flags)
RLAPI void SetConfigFlags(unsigned char flags); // Setup window configuration flags (view FLAGS)
RLAPI void SetTraceLog(unsigned char types); // Enable trace log message types (bit flags based)
+RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set a trace log callback to enable custom logging bypassing raylib's one
RLAPI void TraceLog(int logType, const char *text, ...); // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (saved a .png)
RLAPI int GetRandomValue(int min, int max); // Returns a random value between min and max (both included)
diff --git a/release/libs/win32/mingw32/libraylib.a b/release/libs/win32/mingw32/libraylib.a
index c27c819e..5c64af95 100644
--- a/release/libs/win32/mingw32/libraylib.a
+++ b/release/libs/win32/mingw32/libraylib.a
Binary files differ
diff --git a/src/camera.h b/src/camera.h
index 6bada666..2ed35f3b 100644
--- a/src/camera.h
+++ b/src/camera.h
@@ -244,8 +244,8 @@ void SetCameraMode(Camera camera, int mode)
distance.y = sqrtf(dx*dx + dy*dy);
// Camera angle calculation
- cameraAngle.x = asinf(fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
- cameraAngle.y = -asinf(fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW)
+ cameraAngle.x = asinf( (float)fabs(dx)/distance.x); // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
+ cameraAngle.y = -asinf( (float)fabs(dy)/distance.y); // Camera angle in plane XY (0 aligned with X, move positive CW)
// NOTE: Just testing what cameraAngle means
//cameraAngle.x = 0.0f*DEG2RAD; // Camera angle in plane XZ (0 aligned with Z, move positive CCW)
diff --git a/src/core.c b/src/core.c
index a1a1b1f9..7363a0c1 100644
--- a/src/core.c
+++ b/src/core.c
@@ -886,7 +886,7 @@ void EndDrawing(void)
// Wait for some milliseconds...
if (frameTime < targetTime)
{
- Wait((targetTime - frameTime)*1000.0f);
+ Wait( (float)(targetTime - frameTime)*1000.0f);
currentTime = GetTime();
double extraTime = currentTime - previousTime;
@@ -2510,7 +2510,7 @@ static void SetupFramebufferSize(int displayWidth, int displayHeight)
// Initialize hi-resolution timer
static void InitTimer(void)
{
- srand(time(NULL)); // Initialize random seed
+ srand((unsigned int)time(NULL)); // Initialize random seed
#if !defined(SUPPORT_BUSY_WAIT_LOOP) && defined(_WIN32)
timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms)
diff --git a/src/external/mini_al.c b/src/external/mini_al.c
index 1ce94419..eabb6365 100644
--- a/src/external/mini_al.c
+++ b/src/external/mini_al.c
@@ -1,4 +1,8 @@
// The implementation of mini_al needs to #include windows.h which means it needs to go into
// it's own translation unit. Not doing this will cause conflicts with CloseWindow(), etc.
#define MINI_AL_IMPLEMENTATION
-#include "mini_al.h" \ No newline at end of file
+#define MAL_NO_JACK
+#define MAL_NO_OPENAL
+#define MAL_NO_SDL
+#define MAL_NO_NULL
+#include "mini_al.h"
diff --git a/src/external/mini_al.h b/src/external/mini_al.h
index 4314343d..829a7396 100644
--- a/src/external/mini_al.h
+++ b/src/external/mini_al.h
@@ -1,5 +1,5 @@
// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file.
-// mini_al - v0.8.3 - 2018-07-15
+// mini_al - v0.8.5-rc - 2018-xx-xx
//
// David Reid - [email protected]
@@ -20,7 +20,9 @@
// - ALSA
// - PulseAudio
// - JACK
-// - OSS
+// - sndio (OpenBSD)
+// - audioio (NetBSD)
+// - OSS (FreeBSD)
// - OpenSL|ES (Android only)
// - OpenAL
// - SDL
@@ -65,8 +67,8 @@
//
// Building for BSD
// ----------------
-// The BSD build uses OSS. Requires linking to -lpthread and -lm. Also requires linking to -lossaudio on {Open,Net}BSD,
-// but not FreeBSD.
+// The BSD build only requires linking to -ldl, -lpthread and -lm. NetBSD uses audio(4), OpenBSD uses sndio and
+// FreeBSD uses OSS.
//
// Building for Android
// --------------------
@@ -116,8 +118,9 @@
// - If mal_device_init() is called with a device that's not aligned to the platform's natural alignment
// boundary (4 bytes on 32-bit, 8 bytes on 64-bit), it will _not_ be thread-safe. The reason for this
// is that it depends on members of mal_device being correctly aligned for atomic assignments.
-// - Sample data is always little-endian and interleaved. For example, mal_format_s16 means signed 16-bit
+// - Sample data is always native-endian and interleaved. For example, mal_format_s16 means signed 16-bit
// integer samples, interleaved. Let me know if you need non-interleaved and I'll look into it.
+// - The sndio backend is currently only enabled on OpenBSD builds.
//
//
//
@@ -180,6 +183,12 @@
// #define MAL_NO_COREAUDIO
// Disables the Core Audio backend.
//
+// #define MAL_NO_SNDIO
+// Disables the sndio backend.
+//
+// #define MAL_NO_AUDIOIO
+// Disables the audioio backend.
+//
// #define MAL_NO_OSS
// Disables the OSS backend.
//
@@ -207,6 +216,10 @@
// #define MAL_NO_DECODING
// Disables the decoding APIs.
//
+// #define MAL_NO_DEVICE_IO
+// Disables playback and recording. This will disable mal_context and mal_device APIs. This is useful if you only want to
+// use mini_al's data conversion and/or decoding APIs.
+//
// #define MAL_NO_STDIO
// Disables file IO APIs.
//
@@ -275,82 +288,6 @@ extern "C" {
#endif
#endif
-// Some backends are only supported on certain platforms.
-#if defined(MAL_WIN32)
- #define MAL_SUPPORT_WASAPI
- #if defined(MAL_WIN32_DESKTOP) // DirectSound and WinMM backends are only supported on desktop's.
- #define MAL_SUPPORT_DSOUND
- #define MAL_SUPPORT_WINMM
- #define MAL_SUPPORT_JACK // JACK is technically supported on Windows, but I don't know how many people use it in practice...
- #endif
-#endif
-#if defined(MAL_UNIX)
- #if defined(MAL_LINUX)
- #if !defined(MAL_ANDROID) // ALSA is not supported on Android.
- #define MAL_SUPPORT_ALSA
- #endif
- #endif
- #if !defined(MAL_BSD) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN)
- #define MAL_SUPPORT_PULSEAUDIO
- #define MAL_SUPPORT_JACK
- #endif
- #if defined(MAL_ANDROID)
- #define MAL_SUPPORT_OPENSL
- #endif
- #if !defined(MAL_LINUX) && !defined(MAL_APPLE) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN)
- #define MAL_SUPPORT_OSS
- #endif
-#endif
-#if defined(MAL_APPLE)
- #define MAL_SUPPORT_COREAUDIO
-#endif
-
-#define MAL_SUPPORT_SDL // All platforms support SDL.
-
-// Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now.
-#if !defined(MAL_EMSCRIPTEN)
-#define MAL_SUPPORT_OPENAL
-#define MAL_SUPPORT_NULL // All platforms support the null backend.
-#endif
-
-
-#if !defined(MAL_NO_WASAPI) && defined(MAL_SUPPORT_WASAPI)
- #define MAL_ENABLE_WASAPI
-#endif
-#if !defined(MAL_NO_DSOUND) && defined(MAL_SUPPORT_DSOUND)
- #define MAL_ENABLE_DSOUND
-#endif
-#if !defined(MAL_NO_WINMM) && defined(MAL_SUPPORT_WINMM)
- #define MAL_ENABLE_WINMM
-#endif
-#if !defined(MAL_NO_ALSA) && defined(MAL_SUPPORT_ALSA)
- #define MAL_ENABLE_ALSA
-#endif
-#if !defined(MAL_NO_PULSEAUDIO) && defined(MAL_SUPPORT_PULSEAUDIO)
- #define MAL_ENABLE_PULSEAUDIO
-#endif
-#if !defined(MAL_NO_JACK) && defined(MAL_SUPPORT_JACK)
- #define MAL_ENABLE_JACK
-#endif
-#if !defined(MAL_NO_COREAUDIO) && defined(MAL_SUPPORT_COREAUDIO)
- #define MAL_ENABLE_COREAUDIO
-#endif
-#if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS)
- #define MAL_ENABLE_OSS
-#endif
-#if !defined(MAL_NO_OPENSL) && defined(MAL_SUPPORT_OPENSL)
- #define MAL_ENABLE_OPENSL
-#endif
-#if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL)
- #define MAL_ENABLE_OPENAL
-#endif
-#if !defined(MAL_NO_SDL) && defined(MAL_SUPPORT_SDL)
- #define MAL_ENABLE_SDL
-#endif
-#if !defined(MAL_NO_NULL) && defined(MAL_SUPPORT_NULL)
- #define MAL_ENABLE_NULL
-#endif
-
#include <stddef.h> // For size_t.
#ifndef MAL_HAS_STDINT
@@ -479,122 +416,6 @@ typedef mal_uint16 wchar_t;
#endif
-// Thread priorties should be ordered such that the default priority of the worker thread is 0.
-typedef enum
-{
- mal_thread_priority_idle = -5,
- mal_thread_priority_lowest = -4,
- mal_thread_priority_low = -3,
- mal_thread_priority_normal = -2,
- mal_thread_priority_high = -1,
- mal_thread_priority_highest = 0,
- mal_thread_priority_realtime = 1,
- mal_thread_priority_default = 0
-} mal_thread_priority;
-
-typedef struct
-{
- mal_context* pContext;
-
- union
- {
-#ifdef MAL_WIN32
- struct
- {
- /*HANDLE*/ mal_handle hThread;
- } win32;
-#endif
-#ifdef MAL_POSIX
- struct
- {
- pthread_t thread;
- } posix;
-#endif
-
- int _unused;
- };
-} mal_thread;
-
-typedef struct
-{
- mal_context* pContext;
-
- union
- {
-#ifdef MAL_WIN32
- struct
- {
- /*HANDLE*/ mal_handle hMutex;
- } win32;
-#endif
-#ifdef MAL_POSIX
- struct
- {
- pthread_mutex_t mutex;
- } posix;
-#endif
-
- int _unused;
- };
-} mal_mutex;
-
-typedef struct
-{
- mal_context* pContext;
-
- union
- {
-#ifdef MAL_WIN32
- struct
- {
- /*HANDLE*/ mal_handle hEvent;
- } win32;
-#endif
-#ifdef MAL_POSIX
- struct
- {
- pthread_mutex_t mutex;
- pthread_cond_t condition;
- mal_uint32 value;
- } posix;
-#endif
-
- int _unused;
- };
-} mal_event;
-
-
-#define MAL_MAX_PERIODS_DSOUND 4
-#define MAL_MAX_PERIODS_OPENAL 4
-
-// Standard sample rates.
-#define MAL_SAMPLE_RATE_8000 8000
-#define MAL_SAMPLE_RATE_11025 11025
-#define MAL_SAMPLE_RATE_16000 16000
-#define MAL_SAMPLE_RATE_22050 22050
-#define MAL_SAMPLE_RATE_24000 24000
-#define MAL_SAMPLE_RATE_32000 32000
-#define MAL_SAMPLE_RATE_44100 44100
-#define MAL_SAMPLE_RATE_48000 48000
-#define MAL_SAMPLE_RATE_88200 88200
-#define MAL_SAMPLE_RATE_96000 96000
-#define MAL_SAMPLE_RATE_176400 176400
-#define MAL_SAMPLE_RATE_192000 192000
-#define MAL_SAMPLE_RATE_352800 352800
-#define MAL_SAMPLE_RATE_384000 384000
-
-#define MAL_MIN_PCM_SAMPLE_SIZE_IN_BYTES 1 // For simplicity, mini_al does not support PCM samples that are not byte aligned.
-#define MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES 8
-#define MAL_MIN_CHANNELS 1
-#define MAL_MAX_CHANNELS 32
-#define MAL_MIN_SAMPLE_RATE MAL_SAMPLE_RATE_8000
-#define MAL_MAX_SAMPLE_RATE MAL_SAMPLE_RATE_384000
-#define MAL_SRC_SINC_MIN_WINDOW_WIDTH 2
-#define MAL_SRC_SINC_MAX_WINDOW_WIDTH 32
-#define MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH 16
-#define MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION 8
-#define MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES 256
-
typedef mal_uint8 mal_channel;
#define MAL_CHANNEL_NONE 0
#define MAL_CHANNEL_MONO 1
@@ -688,38 +509,33 @@ typedef int mal_result;
#define MAL_ACCESS_DENIED -32
#define MAL_TOO_LARGE -33
-typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message);
-typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples);
-typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples);
-typedef void (* mal_stop_proc)(mal_device* pDevice);
-
-typedef enum
-{
- mal_backend_null,
- mal_backend_wasapi,
- mal_backend_dsound,
- mal_backend_winmm,
- mal_backend_alsa,
- mal_backend_pulseaudio,
- mal_backend_jack,
- mal_backend_coreaudio,
- mal_backend_oss,
- mal_backend_opensl,
- mal_backend_openal,
- mal_backend_sdl
-} mal_backend;
-
-typedef enum
-{
- mal_device_type_playback,
- mal_device_type_capture
-} mal_device_type;
+// Standard sample rates.
+#define MAL_SAMPLE_RATE_8000 8000
+#define MAL_SAMPLE_RATE_11025 11025
+#define MAL_SAMPLE_RATE_16000 16000
+#define MAL_SAMPLE_RATE_22050 22050
+#define MAL_SAMPLE_RATE_24000 24000
+#define MAL_SAMPLE_RATE_32000 32000
+#define MAL_SAMPLE_RATE_44100 44100
+#define MAL_SAMPLE_RATE_48000 48000
+#define MAL_SAMPLE_RATE_88200 88200
+#define MAL_SAMPLE_RATE_96000 96000
+#define MAL_SAMPLE_RATE_176400 176400
+#define MAL_SAMPLE_RATE_192000 192000
+#define MAL_SAMPLE_RATE_352800 352800
+#define MAL_SAMPLE_RATE_384000 384000
-typedef enum
-{
- mal_share_mode_shared = 0,
- mal_share_mode_exclusive,
-} mal_share_mode;
+#define MAL_MIN_PCM_SAMPLE_SIZE_IN_BYTES 1 // For simplicity, mini_al does not support PCM samples that are not byte aligned.
+#define MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES 8
+#define MAL_MIN_CHANNELS 1
+#define MAL_MAX_CHANNELS 32
+#define MAL_MIN_SAMPLE_RATE MAL_SAMPLE_RATE_8000
+#define MAL_MAX_SAMPLE_RATE MAL_SAMPLE_RATE_384000
+#define MAL_SRC_SINC_MIN_WINDOW_WIDTH 2
+#define MAL_SRC_SINC_MAX_WINDOW_WIDTH 32
+#define MAL_SRC_SINC_DEFAULT_WINDOW_WIDTH 32
+#define MAL_SRC_SINC_LOOKUP_TABLE_RESOLUTION 8
+#define MAL_SRC_INPUT_BUFFER_SIZE_IN_SAMPLES 256
typedef enum
{
@@ -766,6 +582,7 @@ typedef enum
mal_standard_channel_map_rfc3551, // Based off AIFF.
mal_standard_channel_map_flac,
mal_standard_channel_map_vorbis,
+ mal_standard_channel_map_sndio, // www.sndio.org/tips.html
mal_standard_channel_map_default = mal_standard_channel_map_microsoft
} mal_standard_channel_map;
@@ -775,72 +592,6 @@ typedef enum
mal_performance_profile_conservative
} mal_performance_profile;
-typedef union
-{
-#ifdef MAL_SUPPORT_WASAPI
- wchar_t wasapi[64]; // WASAPI uses a wchar_t string for identification.
-#endif
-#ifdef MAL_SUPPORT_DSOUND
- mal_uint8 dsound[16]; // DirectSound uses a GUID for identification.
-#endif
-#ifdef MAL_SUPPORT_WINMM
- /*UINT_PTR*/ mal_uint32 winmm; // When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT.
-#endif
-#ifdef MAL_SUPPORT_ALSA
- char alsa[256]; // ALSA uses a name string for identification.
-#endif
-#ifdef MAL_SUPPORT_PULSEAUDIO
- char pulse[256]; // PulseAudio uses a name string for identification.
-#endif
-#ifdef MAL_SUPPORT_JACK
- int jack; // JACK always uses default devices.
-#endif
-#ifdef MAL_SUPPORT_COREAUDIO
- char coreaudio[256]; // Core Audio uses a string for identification.
-#endif
-#ifdef MAL_SUPPORT_OSS
- char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device.
-#endif
-#ifdef MAL_SUPPORT_OPENSL
- mal_uint32 opensl; // OpenSL|ES uses a 32-bit unsigned integer for identification.
-#endif
-#ifdef MAL_SUPPORT_OPENAL
- char openal[256]; // OpenAL seems to use human-readable device names as the ID.
-#endif
-#ifdef MAL_SUPPORT_SDL
- int sdl; // SDL devices are identified with an index.
-#endif
-#ifdef MAL_SUPPORT_NULL
- int nullbackend; // The null backend uses an integer for device IDs.
-#endif
-} mal_device_id;
-
-typedef struct
-{
- // Basic info. This is the only information guaranteed to be filled in during device enumeration.
- mal_device_id id;
- char name[256];
-
- // Detailed info. As much of this is filled as possible with mal_context_get_device_info(). Note that you are allowed to initialize
- // a device with settings outside of this range, but it just means the data will be converted using mini_al's data conversion
- // pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
- // here mainly for informational purposes or in the rare case that someone might find it useful.
- //
- // These will be set to 0 when returned by mal_context_enumerate_devices() or mal_context_get_devices().
- mal_uint32 formatCount;
- mal_format formats[mal_format_count];
- mal_uint32 minChannels;
- mal_uint32 maxChannels;
- mal_uint32 minSampleRate;
- mal_uint32 maxSampleRate;
-} mal_device_info;
-
-typedef struct
-{
- mal_int64 counter;
-} mal_timer;
-
-
typedef struct mal_format_converter mal_format_converter;
typedef mal_uint32 (* mal_format_converter_read_proc) (mal_format_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData);
@@ -931,6 +682,12 @@ typedef enum
typedef struct
{
+ mal_src_sinc_window_function windowFunction;
+ mal_uint32 windowWidth;
+} mal_src_config_sinc;
+
+typedef struct
+{
mal_uint32 sampleRateIn;
mal_uint32 sampleRateOut;
mal_uint32 channels;
@@ -944,11 +701,7 @@ typedef struct
void* pUserData;
union
{
- struct
- {
- mal_src_sinc_window_function windowFunction;
- mal_uint32 windowWidth;
- } sinc;
+ mal_src_config_sinc sinc;
};
} mal_src_config;
@@ -1007,11 +760,7 @@ typedef struct
void* pUserData;
union
{
- struct
- {
- mal_src_sinc_window_function windowFunction;
- mal_uint32 windowWidth;
- } sinc;
+ mal_src_config_sinc sinc;
};
} mal_dsp_config;
@@ -1033,6 +782,637 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_dsp
};
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// DATA CONVERSION
+// ===============
+//
+// This section contains the APIs for data conversion. You will find everything here for channel mapping, sample format conversion, resampling, etc.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Channel Maps
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Helper for retrieving a standard channel map.
+void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]);
+
+// Copies a channel map.
+void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels);
+
+
+// Determines whether or not a channel map is valid.
+//
+// A blank channel map is valid (all channels set to MAL_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
+// is usually treated as a passthrough.
+//
+// Invalid channel maps:
+// - A channel map with no channels
+// - A channel map with more than one channel and a mono channel
+mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]);
+
+// Helper for comparing two channel maps for equality.
+//
+// This assumes the channel count is the same between the two.
+mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS]);
+
+// Helper for determining if a channel map is blank (all channels set to MAL_CHANNEL_NONE).
+mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]);
+
+// Helper for determining whether or not a channel is present in the given channel map.
+mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition);
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Format Conversion
+// =================
+// The format converter serves two purposes:
+// 1) Conversion between data formats (u8 to f32, etc.)
+// 2) Interleaving and deinterleaving
+//
+// When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for
+// interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You
+// can implement both, but it's not recommended as it just introduces unnecessary complexity.
+//
+// To read data as interleaved samples, use mal_format_converter_read(). Otherwise use mal_format_converter_read_deinterleaved().
+//
+// Dithering
+// ---------
+// The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so.
+//
+// pConfig->ditherMode = mal_dither_mode_rectangle;
+//
+// The different dithering modes include the following, in order of efficiency:
+// - None: mal_dither_mode_none
+// - Rectangle: mal_dither_mode_rectangle
+// - Triangle: mal_dither_mode_triangle
+//
+// Note that even if the dither mode is set to something other than mal_dither_mode_none, it will be ignored for conversions where dithering is not needed.
+// Dithering is available for the following conversions:
+// - s16 -> u8
+// - s24 -> u8
+// - s32 -> u8
+// - f32 -> u8
+// - s24 -> s16
+// - s32 -> s16
+// - f32 -> s16
+//
+// Note that it is not an error to pass something other than mal_dither_mode_none for conversions where dither is not used. It will just be ignored.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Initializes a format converter.
+mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter* pConverter);
+
+// Reads data from the format converter as interleaved channels.
+mal_uint64 mal_format_converter_read(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut, void* pUserData);
+
+// Reads data from the format converter as deinterleaved channels.
+mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
+
+
+// Helper for initializing a format converter config.
+mal_format_converter_config mal_format_converter_config_init_new(void);
+mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData);
+mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Channel Routing
+// ===============
+// There are two main things you can do with the channel router:
+// 1) Rearrange channels
+// 2) Convert from one channel count to another
+//
+// Channel Rearrangement
+// ---------------------
+// A simple example of channel rearrangement may be swapping the left and right channels in a stereo stream. To do this you just pass in the same channel
+// count for both the input and output with channel maps that contain the same channels (in a different order).
+//
+// Channel Conversion
+// ------------------
+// The channel router can also convert from one channel count to another, such as converting a 5.1 stream to stero. When changing the channel count, the
+// router will first perform a 1:1 mapping of channel positions that are present in both the input and output channel maps. The second thing it will do
+// is distribute the input mono channel (if any) across all output channels, excluding any None and LFE channels. If there is an output mono channel, all
+// input channels will be averaged, excluding any None and LFE channels.
+//
+// The last case to consider is when a channel position in the input channel map is not present in the output channel map, and vice versa. In this case the
+// channel router will perform a blend of other related channels to produce an audible channel. There are several blending modes.
+// 1) Simple
+// Unmatched channels are silenced.
+// 2) Planar Blending
+// Channels are blended based on a set of planes that each speaker emits audio from.
+//
+// Planar Blending
+// ---------------
+// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker.
+// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left
+// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map
+// contains only the front/left channel position, but the output channel map contains both the front/left and front/center channel. When deciding on the audio
+// data to send to the front/center speaker (which has no 1:1 mapping with an input channel) we need to use some logic based on our available input channel
+// positions.
+//
+// As mentioned earlier, our front/left speaker is, conceptually speaking, emitting audio from the front _and_ the left planes. Similarly, the front/center
+// speaker is emitting audio from _only_ the front plane. What these two channels have in common is that they are both emitting audio from the front plane.
+// Thus, it makes sense that the front/center speaker should receive some contribution from the front/left channel. How much contribution depends on their
+// planar relationship (thus the name of this blending technique).
+//
+// Because the front/left channel is emitting audio from two planes (front and left), you can think of it as though it's willing to dedicate 50% of it's total
+// volume to each of it's planes (a channel position emitting from 1 plane would be willing to given 100% of it's total volume to that plane, and a channel
+// position emitting from 3 planes would be willing to given 33% of it's total volume to each plane). Similarly, the front/center speaker is emitting audio
+// from only one plane so you can think of it as though it's willing to _take_ 100% of it's volume from front plane emissions. Now, since the front/left
+// channel is willing to _give_ 50% of it's total volume to the front plane, and the front/center speaker is willing to _take_ 100% of it's total volume
+// from the front, you can imagine that 50% of the front/left speaker will be given to the front/center speaker.
+//
+// Usage
+// -----
+// To use the channel router you need to specify three things:
+// 1) The input channel count and channel map
+// 2) The output channel count and channel map
+// 3) The mixing mode to use in the case where a 1:1 mapping is unavailable
+//
+// Note that input and output data is always deinterleaved 32-bit floating point.
+//
+// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration,
+// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing.
+//
+// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Initializes a channel router where it is assumed that the input data is non-interleaved.
+mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal_channel_router* pRouter);
+
+// Reads data from the channel router as deinterleaved channels.
+mal_uint64 mal_channel_router_read_deinterleaved(mal_channel_router* pRouter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
+
+// Helper for initializing a channel router config.
+mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn, const mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint32 channelsOut, const mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_channel_mix_mode mixingMode, mal_channel_router_read_deinterleaved_proc onRead, void* pUserData);
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Sample Rate Conversion
+// ======================
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Initializes a sample rate conversion object.
+mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC);
+
+// Dynamically adjusts the input sample rate.
+//
+// DEPRECATED. Use mal_src_set_sample_rate() instead.
+mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn);
+
+// Dynamically adjusts the output sample rate.
+//
+// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
+// is not acceptable you will need to use your own algorithm.
+//
+// DEPRECATED. Use mal_src_set_sample_rate() instead.
+mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut);
+
+// Dynamically adjusts the sample rate.
+//
+// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
+// is not acceptable you will need to use your own algorithm.
+mal_result mal_src_set_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut);
+
+// Reads a number of frames.
+//
+// Returns the number of frames actually read.
+mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
+
+
+// Helper for creating a sample rate conversion config.
+mal_src_config mal_src_config_init_new(void);
+mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// DSP
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// Initializes a DSP object.
+mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP);
+
+// Dynamically adjusts the input sample rate.
+//
+// This will fail is the DSP was not initialized with allowDynamicSampleRate.
+//
+// DEPRECATED. Use mal_dsp_set_sample_rate() instead.
+mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut);
+
+// Dynamically adjusts the output sample rate.
+//
+// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
+// is not acceptable you will need to use your own algorithm.
+//
+// This will fail is the DSP was not initialized with allowDynamicSampleRate.
+//
+// DEPRECATED. Use mal_dsp_set_sample_rate() instead.
+mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut);
+
+// Dynamically adjusts the output sample rate.
+//
+// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
+// is not acceptable you will need to use your own algorithm.
+//
+// This will fail is the DSP was not initialized with allowDynamicSampleRate.
+mal_result mal_dsp_set_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut);
+
+
+// Reads a number of frames and runs them through the DSP processor.
+mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData);
+
+// Helper for initializing a mal_dsp_config object.
+mal_dsp_config mal_dsp_config_init_new(void);
+mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_dsp_read_proc onRead, void* pUserData);
+mal_dsp_config mal_dsp_config_init_ex(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_dsp_read_proc onRead, void* pUserData);
+
+
+// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to
+// determine the required size of the output buffer.
+//
+// A return value of 0 indicates an error.
+//
+// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead.
+mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn);
+mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint64 frameCountIn);
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Miscellaneous Helpers
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+// malloc(). Calls MAL_MALLOC().
+void* mal_malloc(size_t sz);
+
+// realloc(). Calls MAL_REALLOC().
+void* mal_realloc(void* p, size_t sz);
+
+// free(). Calls MAL_FREE().
+void mal_free(void* p);
+
+// Performs an aligned malloc, with the assumption that the alignment is a power of 2.
+void* mal_aligned_malloc(size_t sz, size_t alignment);
+
+// Free's an aligned malloc'd buffer.
+void mal_aligned_free(void* p);
+
+// Retrieves a friendly name for a format.
+const char* mal_get_format_name(mal_format format);
+
+// Blends two frames in floating point format.
+void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels);
+
+// Retrieves the size of a sample in bytes for the given format.
+//
+// This API is efficient and is implemented using a lookup table.
+//
+// Thread Safety: SAFE
+// This is API is pure.
+mal_uint32 mal_get_bytes_per_sample(mal_format format);
+static MAL_INLINE mal_uint32 mal_get_bytes_per_frame(mal_format format, mal_uint32 channels) { return mal_get_bytes_per_sample(format) * channels; }
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Format Conversion
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+void mal_pcm_u8_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_u8_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_u8_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_u8_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s16_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s16_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s16_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s16_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s24_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s24_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s24_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s24_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_s32_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_f32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_f32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
+void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
+
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// DEVICE I/O
+// ==========
+//
+// This section contains the APIs for device playback and capture. Here is where you'll find mal_device_init(), etc.
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#ifndef MAL_NO_DEVICE_IO
+// Some backends are only supported on certain platforms.
+#if defined(MAL_WIN32)
+ #define MAL_SUPPORT_WASAPI
+ #if defined(MAL_WIN32_DESKTOP) // DirectSound and WinMM backends are only supported on desktop's.
+ #define MAL_SUPPORT_DSOUND
+ #define MAL_SUPPORT_WINMM
+ #define MAL_SUPPORT_JACK // JACK is technically supported on Windows, but I don't know how many people use it in practice...
+ #endif
+#endif
+#if defined(MAL_UNIX)
+ #if defined(MAL_LINUX)
+ #if !defined(MAL_ANDROID) // ALSA is not supported on Android.
+ #define MAL_SUPPORT_ALSA
+ #endif
+ #endif
+ #if !defined(MAL_BSD) && !defined(MAL_ANDROID) && !defined(MAL_EMSCRIPTEN)
+ #define MAL_SUPPORT_PULSEAUDIO
+ #define MAL_SUPPORT_JACK
+ #endif
+ #if defined(MAL_ANDROID)
+ #define MAL_SUPPORT_OPENSL
+ #endif
+ #if defined(__OpenBSD__) // <-- Change this to "#if defined(MAL_BSD)" to enable sndio on all BSD flavors.
+ #define MAL_SUPPORT_SNDIO // sndio is only supported on OpenBSD for now. May be expanded later if there's demand.
+ #endif
+ #if defined(__NetBSD__)
+ #define MAL_SUPPORT_AUDIOIO // Only support audioio on platforms with known support.
+ #endif
+ #if defined(__FreeBSD__) || defined(__DragonFly__)
+ #define MAL_SUPPORT_OSS // Only support OSS on specific platforms with known support.
+ #endif
+#endif
+#if defined(MAL_APPLE)
+ #define MAL_SUPPORT_COREAUDIO
+#endif
+
+#define MAL_SUPPORT_SDL // All platforms support SDL.
+
+// Explicitly disable OpenAL and Null backends for Emscripten because they both use a background thread which is not properly supported right now.
+#if !defined(MAL_EMSCRIPTEN)
+#define MAL_SUPPORT_OPENAL
+#define MAL_SUPPORT_NULL // All platforms support the null backend.
+#endif
+
+
+#if !defined(MAL_NO_WASAPI) && defined(MAL_SUPPORT_WASAPI)
+ #define MAL_ENABLE_WASAPI
+#endif
+#if !defined(MAL_NO_DSOUND) && defined(MAL_SUPPORT_DSOUND)
+ #define MAL_ENABLE_DSOUND
+#endif
+#if !defined(MAL_NO_WINMM) && defined(MAL_SUPPORT_WINMM)
+ #define MAL_ENABLE_WINMM
+#endif
+#if !defined(MAL_NO_ALSA) && defined(MAL_SUPPORT_ALSA)
+ #define MAL_ENABLE_ALSA
+#endif
+#if !defined(MAL_NO_PULSEAUDIO) && defined(MAL_SUPPORT_PULSEAUDIO)
+ #define MAL_ENABLE_PULSEAUDIO
+#endif
+#if !defined(MAL_NO_JACK) && defined(MAL_SUPPORT_JACK)
+ #define MAL_ENABLE_JACK
+#endif
+#if !defined(MAL_NO_COREAUDIO) && defined(MAL_SUPPORT_COREAUDIO)
+ #define MAL_ENABLE_COREAUDIO
+#endif
+#if !defined(MAL_NO_SNDIO) && defined(MAL_SUPPORT_SNDIO)
+ #define MAL_ENABLE_SNDIO
+#endif
+#if !defined(MAL_NO_AUDIOIO) && defined(MAL_SUPPORT_AUDIOIO)
+ #define MAL_ENABLE_AUDIOIO
+#endif
+#if !defined(MAL_NO_OSS) && defined(MAL_SUPPORT_OSS)
+ #define MAL_ENABLE_OSS
+#endif
+#if !defined(MAL_NO_OPENSL) && defined(MAL_SUPPORT_OPENSL)
+ #define MAL_ENABLE_OPENSL
+#endif
+#if !defined(MAL_NO_OPENAL) && defined(MAL_SUPPORT_OPENAL)
+ #define MAL_ENABLE_OPENAL
+#endif
+#if !defined(MAL_NO_SDL) && defined(MAL_SUPPORT_SDL)
+ #define MAL_ENABLE_SDL
+#endif
+#if !defined(MAL_NO_NULL) && defined(MAL_SUPPORT_NULL)
+ #define MAL_ENABLE_NULL
+#endif
+
+
+typedef enum
+{
+ mal_backend_null,
+ mal_backend_wasapi,
+ mal_backend_dsound,
+ mal_backend_winmm,
+ mal_backend_alsa,
+ mal_backend_pulseaudio,
+ mal_backend_jack,
+ mal_backend_coreaudio,
+ mal_backend_sndio,
+ mal_backend_audioio,
+ mal_backend_oss,
+ mal_backend_opensl,
+ mal_backend_openal,
+ mal_backend_sdl
+} mal_backend;
+
+// Thread priorties should be ordered such that the default priority of the worker thread is 0.
+typedef enum
+{
+ mal_thread_priority_idle = -5,
+ mal_thread_priority_lowest = -4,
+ mal_thread_priority_low = -3,
+ mal_thread_priority_normal = -2,
+ mal_thread_priority_high = -1,
+ mal_thread_priority_highest = 0,
+ mal_thread_priority_realtime = 1,
+ mal_thread_priority_default = 0
+} mal_thread_priority;
+
+typedef struct
+{
+ mal_context* pContext;
+
+ union
+ {
+#ifdef MAL_WIN32
+ struct
+ {
+ /*HANDLE*/ mal_handle hThread;
+ } win32;
+#endif
+#ifdef MAL_POSIX
+ struct
+ {
+ pthread_t thread;
+ } posix;
+#endif
+
+ int _unused;
+ };
+} mal_thread;
+
+typedef struct
+{
+ mal_context* pContext;
+
+ union
+ {
+#ifdef MAL_WIN32
+ struct
+ {
+ /*HANDLE*/ mal_handle hMutex;
+ } win32;
+#endif
+#ifdef MAL_POSIX
+ struct
+ {
+ pthread_mutex_t mutex;
+ } posix;
+#endif
+
+ int _unused;
+ };
+} mal_mutex;
+
+typedef struct
+{
+ mal_context* pContext;
+
+ union
+ {
+#ifdef MAL_WIN32
+ struct
+ {
+ /*HANDLE*/ mal_handle hEvent;
+ } win32;
+#endif
+#ifdef MAL_POSIX
+ struct
+ {
+ pthread_mutex_t mutex;
+ pthread_cond_t condition;
+ mal_uint32 value;
+ } posix;
+#endif
+
+ int _unused;
+ };
+} mal_event;
+
+
+#define MAL_MAX_PERIODS_DSOUND 4
+#define MAL_MAX_PERIODS_OPENAL 4
+
+typedef void (* mal_log_proc) (mal_context* pContext, mal_device* pDevice, const char* message);
+typedef void (* mal_recv_proc)(mal_device* pDevice, mal_uint32 frameCount, const void* pSamples);
+typedef mal_uint32 (* mal_send_proc)(mal_device* pDevice, mal_uint32 frameCount, void* pSamples);
+typedef void (* mal_stop_proc)(mal_device* pDevice);
+
+typedef enum
+{
+ mal_device_type_playback,
+ mal_device_type_capture
+} mal_device_type;
+
+typedef enum
+{
+ mal_share_mode_shared = 0,
+ mal_share_mode_exclusive,
+} mal_share_mode;
+
+typedef union
+{
+#ifdef MAL_SUPPORT_WASAPI
+ wchar_t wasapi[64]; // WASAPI uses a wchar_t string for identification.
+#endif
+#ifdef MAL_SUPPORT_DSOUND
+ mal_uint8 dsound[16]; // DirectSound uses a GUID for identification.
+#endif
+#ifdef MAL_SUPPORT_WINMM
+ /*UINT_PTR*/ mal_uint32 winmm; // When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT.
+#endif
+#ifdef MAL_SUPPORT_ALSA
+ char alsa[256]; // ALSA uses a name string for identification.
+#endif
+#ifdef MAL_SUPPORT_PULSEAUDIO
+ char pulse[256]; // PulseAudio uses a name string for identification.
+#endif
+#ifdef MAL_SUPPORT_JACK
+ int jack; // JACK always uses default devices.
+#endif
+#ifdef MAL_SUPPORT_COREAUDIO
+ char coreaudio[256]; // Core Audio uses a string for identification.
+#endif
+#ifdef MAL_SUPPORT_SNDIO
+ char sndio[256]; // "snd/0", etc.
+#endif
+#ifdef MAL_SUPPORT_AUDIOIO
+ char audioio[256]; // "/dev/audio", etc.
+#endif
+#ifdef MAL_SUPPORT_OSS
+ char oss[64]; // "dev/dsp0", etc. "dev/dsp" for the default device.
+#endif
+#ifdef MAL_SUPPORT_OPENSL
+ mal_uint32 opensl; // OpenSL|ES uses a 32-bit unsigned integer for identification.
+#endif
+#ifdef MAL_SUPPORT_OPENAL
+ char openal[256]; // OpenAL seems to use human-readable device names as the ID.
+#endif
+#ifdef MAL_SUPPORT_SDL
+ int sdl; // SDL devices are identified with an index.
+#endif
+#ifdef MAL_SUPPORT_NULL
+ int nullbackend; // The null backend uses an integer for device IDs.
+#endif
+} mal_device_id;
+
+typedef struct
+{
+ // Basic info. This is the only information guaranteed to be filled in during device enumeration.
+ mal_device_id id;
+ char name[256];
+
+ // Detailed info. As much of this is filled as possible with mal_context_get_device_info(). Note that you are allowed to initialize
+ // a device with settings outside of this range, but it just means the data will be converted using mini_al's data conversion
+ // pipeline before sending the data to/from the device. Most programs will need to not worry about these values, but it's provided
+ // here mainly for informational purposes or in the rare case that someone might find it useful.
+ //
+ // These will be set to 0 when returned by mal_context_enumerate_devices() or mal_context_get_devices().
+ mal_uint32 formatCount;
+ mal_format formats[mal_format_count];
+ mal_uint32 minChannels;
+ mal_uint32 maxChannels;
+ mal_uint32 minSampleRate;
+ mal_uint32 maxSampleRate;
+} mal_device_info;
+
+typedef struct
+{
+ mal_int64 counter;
+} mal_timer;
+
typedef struct
{
mal_format format;
@@ -1299,6 +1679,35 @@ struct mal_context
mal_proc AudioUnitRender;
} coreaudio;
#endif
+#ifdef MAL_SUPPORT_SNDIO
+ struct
+ {
+ mal_handle sndioSO;
+ mal_proc sio_open;
+ mal_proc sio_close;
+ mal_proc sio_setpar;
+ mal_proc sio_getpar;
+ mal_proc sio_getcap;
+ mal_proc sio_start;
+ mal_proc sio_stop;
+ mal_proc sio_read;
+ mal_proc sio_write;
+ mal_proc sio_onmove;
+ mal_proc sio_nfds;
+ mal_proc sio_pollfd;
+ mal_proc sio_revents;
+ mal_proc sio_eof;
+ mal_proc sio_setvol;
+ mal_proc sio_onvol;
+ mal_proc sio_initpar;
+ } sndio;
+#endif
+#ifdef MAL_SUPPORT_AUDIOIO
+ struct
+ {
+ int _unused;
+ } audioio;
+#endif
#ifdef MAL_SUPPORT_OSS
struct
{
@@ -1591,6 +2000,24 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
/*AudioBufferList**/ mal_ptr pAudioBufferList; // Only used for input devices.
} coreaudio;
#endif
+#ifdef MAL_SUPPORT_SNDIO
+ struct
+ {
+ mal_ptr handle;
+ mal_uint32 fragmentSizeInFrames;
+ mal_bool32 breakFromMainLoop;
+ void* pIntermediaryBuffer;
+ } sndio;
+#endif
+#ifdef MAL_SUPPORT_AUDIOIO
+ struct
+ {
+ int fd;
+ mal_uint32 fragmentSizeInFrames;
+ mal_bool32 breakFromMainLoop;
+ void* pIntermediaryBuffer;
+ } audioio;
+#endif
#ifdef MAL_SUPPORT_OSS
struct
{
@@ -1663,7 +2090,9 @@ MAL_ALIGNED_STRUCT(MAL_SIMD_ALIGNMENT) mal_device
// - WASAPI
// - DirectSound
// - WinMM
-// - Core Audio (macOS, iOS)
+// - Core Audio (Apple)
+// - sndio
+// - audioio
// - OSS
// - PulseAudio
// - ALSA
@@ -1941,15 +2370,6 @@ mal_bool32 mal_device_is_started(mal_device* pDevice);
// This is calculated from constant values which are set at initialization time and never change.
mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice);
-// Retrieves the size of a sample in bytes for the given format.
-//
-// This API is efficient and is implemented using a lookup table.
-//
-// Thread Safety: SAFE
-// This is API is pure.
-mal_uint32 mal_get_bytes_per_sample(mal_format format);
-static MAL_INLINE mal_uint32 mal_get_bytes_per_frame(mal_format format, mal_uint32 channels) { return mal_get_bytes_per_sample(format) * channels; }
-
// Helper function for initializing a mal_context_config object.
mal_context_config mal_context_config_init(mal_log_proc onLog);
@@ -2044,236 +2464,6 @@ static MAL_INLINE mal_device_config mal_device_config_init_playback_ex(mal_forma
static MAL_INLINE mal_device_config mal_device_config_init_playback(mal_format format, mal_uint32 channels, mal_uint32 sampleRate, mal_send_proc onSendCallback) { return mal_device_config_init_playback_ex(format, channels, sampleRate, NULL, onSendCallback); }
-// Helper for retrieving a standard channel map.
-void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]);
-
-// Copies a channel map.
-void mal_channel_map_copy(mal_channel* pOut, const mal_channel* pIn, mal_uint32 channels);
-
-
-// Determines whether or not a channel map is valid.
-//
-// A blank channel map is valid (all channels set to MAL_CHANNEL_NONE). The way a blank channel map is handled is context specific, but
-// is usually treated as a passthrough.
-//
-// Invalid channel maps:
-// - A channel map with no channels
-// - A channel map with more than one channel and a mono channel
-mal_bool32 mal_channel_map_valid(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]);
-
-// Helper for comparing two channel maps for equality.
-//
-// This assumes the channel count is the same between the two.
-mal_bool32 mal_channel_map_equal(mal_uint32 channels, const mal_channel channelMapA[MAL_MAX_CHANNELS], const mal_channel channelMapB[MAL_MAX_CHANNELS]);
-
-// Helper for determining if a channel map is blank (all channels set to MAL_CHANNEL_NONE).
-mal_bool32 mal_channel_map_blank(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS]);
-
-// Helper for determining whether or not a channel is present in the given channel map.
-mal_bool32 mal_channel_map_contains_channel_position(mal_uint32 channels, const mal_channel channelMap[MAL_MAX_CHANNELS], mal_channel channelPosition);
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Format Conversion
-// =================
-// The format converter serves two purposes:
-// 1) Conversion between data formats (u8 to f32, etc.)
-// 2) Interleaving and deinterleaving
-//
-// When initializing a converter, you specify the input and output formats (u8, s16, etc.) and read callbacks. There are two read callbacks - one for
-// interleaved input data (onRead) and another for deinterleaved input data (onReadDeinterleaved). You implement whichever is most convenient for you. You
-// can implement both, but it's not recommended as it just introduces unnecessary complexity.
-//
-// To read data as interleaved samples, use mal_format_converter_read(). Otherwise use mal_format_converter_read_deinterleaved().
-//
-// Dithering
-// ---------
-// The format converter also supports dithering. Dithering can be set using ditherMode variable in the config, like so.
-//
-// pConfig->ditherMode = mal_dither_mode_rectangle;
-//
-// The different dithering modes include the following, in order of efficiency:
-// - None: mal_dither_mode_none
-// - Rectangle: mal_dither_mode_rectangle
-// - Triangle: mal_dither_mode_triangle
-//
-// Note that even if the dither mode is set to something other than mal_dither_mode_none, it will be ignored for conversions where dithering is not needed.
-// Dithering is available for the following conversions:
-// - s16 -> u8
-// - s24 -> u8
-// - s32 -> u8
-// - f32 -> u8
-// - s24 -> s16
-// - s32 -> s16
-// - f32 -> s16
-//
-// Note that it is not an error to pass something other than mal_dither_mode_none for conversions where dither is not used. It will just be ignored.
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Initializes a format converter.
-mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter* pConverter);
-
-// Reads data from the format converter as interleaved channels.
-mal_uint64 mal_format_converter_read(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut, void* pUserData);
-
-// Reads data from the format converter as deinterleaved channels.
-mal_uint64 mal_format_converter_read_deinterleaved(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
-
-
-// Helper for initializing a format converter config.
-mal_format_converter_config mal_format_converter_config_init_new(void);
-mal_format_converter_config mal_format_converter_config_init(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_proc onRead, void* pUserData);
-mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_format formatIn, mal_format formatOut, mal_uint32 channels, mal_format_converter_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
-
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Channel Routing
-// ===============
-// There are two main things you can do with the channel router:
-// 1) Rearrange channels
-// 2) Convert from one channel count to another
-//
-// Channel Rearrangement
-// ---------------------
-// A simple example of channel rearrangement may be swapping the left and right channels in a stereo stream. To do this you just pass in the same channel
-// count for both the input and output with channel maps that contain the same channels (in a different order).
-//
-// Channel Conversion
-// ------------------
-// The channel router can also convert from one channel count to another, such as converting a 5.1 stream to stero. When changing the channel count, the
-// router will first perform a 1:1 mapping of channel positions that are present in both the input and output channel maps. The second thing it will do
-// is distribute the input mono channel (if any) across all output channels, excluding any None and LFE channels. If there is an output mono channel, all
-// input channels will be averaged, excluding any None and LFE channels.
-//
-// The last case to consider is when a channel position in the input channel map is not present in the output channel map, and vice versa. In this case the
-// channel router will perform a blend of other related channels to produce an audible channel. There are several blending modes.
-// 1) Simple
-// Unmatched channels are silenced.
-// 2) Planar Blending
-// Channels are blended based on a set of planes that each speaker emits audio from.
-//
-// Planar Blending
-// ---------------
-// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker.
-// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left
-// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map
-// contains only the front/left channel position, but the output channel map contains both the front/left and front/center channel. When deciding on the audio
-// data to send to the front/center speaker (which has no 1:1 mapping with an input channel) we need to use some logic based on our available input channel
-// positions.
-//
-// As mentioned earlier, our front/left speaker is, conceptually speaking, emitting audio from the front _and_ the left planes. Similarly, the front/center
-// speaker is emitting audio from _only_ the front plane. What these two channels have in common is that they are both emitting audio from the front plane.
-// Thus, it makes sense that the front/center speaker should receive some contribution from the front/left channel. How much contribution depends on their
-// planar relationship (thus the name of this blending technique).
-//
-// Because the front/left channel is emitting audio from two planes (front and left), you can think of it as though it's willing to dedicate 50% of it's total
-// volume to each of it's planes (a channel position emitting from 1 plane would be willing to given 100% of it's total volume to that plane, and a channel
-// position emitting from 3 planes would be willing to given 33% of it's total volume to each plane). Similarly, the front/center speaker is emitting audio
-// from only one plane so you can think of it as though it's willing to _take_ 100% of it's volume from front plane emissions. Now, since the front/left
-// channel is willing to _give_ 50% of it's total volume to the front plane, and the front/center speaker is willing to _take_ 100% of it's total volume
-// from the front, you can imagine that 50% of the front/left speaker will be given to the front/center speaker.
-//
-// Usage
-// -----
-// To use the channel router you need to specify three things:
-// 1) The input channel count and channel map
-// 2) The output channel count and channel map
-// 3) The mixing mode to use in the case where a 1:1 mapping is unavailable
-//
-// Note that input and output data is always deinterleaved 32-bit floating point.
-//
-// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration,
-// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing.
-//
-// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point.
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Initializes a channel router where it is assumed that the input data is non-interleaved.
-mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal_channel_router* pRouter);
-
-// Reads data from the channel router as deinterleaved channels.
-mal_uint64 mal_channel_router_read_deinterleaved(mal_channel_router* pRouter, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
-
-// Helper for initializing a channel router config.
-mal_channel_router_config mal_channel_router_config_init(mal_uint32 channelsIn, const mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint32 channelsOut, const mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_channel_mix_mode mixingMode, mal_channel_router_read_deinterleaved_proc onRead, void* pUserData);
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Sample Rate Conversion
-// ======================
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Initializes a sample rate conversion object.
-mal_result mal_src_init(const mal_src_config* pConfig, mal_src* pSRC);
-
-// Dynamically adjusts the input sample rate.
-mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn);
-
-// Dynamically adjusts the output sample rate.
-//
-// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
-// is not acceptable you will need to use your own algorithm.
-mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut);
-
-// Reads a number of frames.
-//
-// Returns the number of frames actually read.
-mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData);
-
-
-// Helper for creating a sample rate conversion config.
-mal_src_config mal_src_config_init_new(void);
-mal_src_config mal_src_config_init(mal_uint32 sampleRateIn, mal_uint32 sampleRateOut, mal_uint32 channels, mal_src_read_deinterleaved_proc onReadDeinterleaved, void* pUserData);
-
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// DSP
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// Initializes a DSP object.
-mal_result mal_dsp_init(const mal_dsp_config* pConfig, mal_dsp* pDSP);
-
-// Dynamically adjusts the input sample rate.
-//
-// This will fail is the DSP was not initialized with allowDynamicSampleRate.
-mal_result mal_dsp_set_input_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut);
-
-// Dynamically adjusts the output sample rate.
-//
-// This is useful for dynamically adjust pitch. Keep in mind, however, that this will speed up or slow down the sound. If this
-// is not acceptable you will need to use your own algorithm.
-//
-// This will fail is the DSP was not initialized with allowDynamicSampleRate.
-mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOut);
-
-// Reads a number of frames and runs them through the DSP processor.
-mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData);
-
-// Helper for initializing a mal_dsp_config object.
-mal_dsp_config mal_dsp_config_init_new(void);
-mal_dsp_config mal_dsp_config_init(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_dsp_read_proc onRead, void* pUserData);
-mal_dsp_config mal_dsp_config_init_ex(mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], mal_dsp_read_proc onRead, void* pUserData);
-
-
-// High-level helper for doing a full format conversion in one go. Returns the number of output frames. Call this with pOut set to NULL to
-// determine the required size of the output buffer.
-//
-// A return value of 0 indicates an error.
-//
-// This function is useful for one-off bulk conversions, but if you're streaming data you should use the DSP APIs instead.
-mal_uint64 mal_convert_frames(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_uint64 frameCountIn);
-mal_uint64 mal_convert_frames_ex(void* pOut, mal_format formatOut, mal_uint32 channelsOut, mal_uint32 sampleRateOut, mal_channel channelMapOut[MAL_MAX_CHANNELS], const void* pIn, mal_format formatIn, mal_uint32 channelsIn, mal_uint32 sampleRateIn, mal_channel channelMapIn[MAL_MAX_CHANNELS], mal_uint64 frameCountIn);
-
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
@@ -2296,37 +2486,9 @@ void mal_mutex_lock(mal_mutex* pMutex);
void mal_mutex_unlock(mal_mutex* pMutex);
-
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Miscellaneous Helpers
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
-// malloc(). Calls MAL_MALLOC().
-void* mal_malloc(size_t sz);
-
-// realloc(). Calls MAL_REALLOC().
-void* mal_realloc(void* p, size_t sz);
-
-// free(). Calls MAL_FREE().
-void mal_free(void* p);
-
-// Performs an aligned malloc, with the assumption that the alignment is a power of 2.
-void* mal_aligned_malloc(size_t sz, size_t alignment);
-
-// Free's an aligned malloc'd buffer.
-void mal_aligned_free(void* p);
-
// Retrieves a friendly name for a backend.
const char* mal_get_backend_name(mal_backend backend);
-// Retrieves a friendly name for a format.
-const char* mal_get_format_name(mal_format format);
-
-// Blends two frames in floating point format.
-void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint32 channels);
-
// Calculates a scaling factor relative to speed of the system.
//
// This could be useful for dynamically determining the size of a device's internal buffer based on the speed of the system.
@@ -2342,35 +2504,9 @@ mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale);
// Calculates a buffer size in frames for the specified performance profile and scale factor.
mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale);
+#endif // MAL_NO_DEVICE_IO
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Format Conversion
-//
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-void mal_pcm_u8_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_u8_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_u8_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_u8_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s16_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s16_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s16_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s16_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s24_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s24_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s24_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s24_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_s32_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_f32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_f32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode);
-void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode);
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -2395,10 +2531,17 @@ typedef mal_result (* mal_decoder_uninit_proc) (mal_decoder* pDecoder);
typedef struct
{
- mal_format format; // Set to 0 or mal_format_unknown to use the stream's internal format.
- mal_uint32 channels; // Set to 0 to use the stream's internal channels.
- mal_uint32 sampleRate; // Set to 0 to use the stream's internal sample rate.
+ mal_format format; // Set to 0 or mal_format_unknown to use the stream's internal format.
+ mal_uint32 channels; // Set to 0 to use the stream's internal channels.
+ mal_uint32 sampleRate; // Set to 0 to use the stream's internal sample rate.
mal_channel channelMap[MAL_MAX_CHANNELS];
+ mal_channel_mix_mode channelMixMode;
+ mal_dither_mode ditherMode;
+ mal_src_algorithm srcAlgorithm;
+ union
+ {
+ mal_src_config_sinc sinc;
+ } src;
} mal_decoder_config;
struct mal_decoder
@@ -2660,9 +2803,23 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSignWave, mal_uint64 count, float*
#elif (defined(__GNUC__) || defined(__clang__)) && !defined(MAL_ANDROID)
static MAL_INLINE void mal_cpuid(int info[4], int fid)
{
- __asm__ __volatile__ (
- "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
- );
+ // It looks like the -fPIC option uses the ebx register which GCC complains about. We can work around this by just using a different register, the
+ // specific register of which I'm letting the compiler decide on. The "k" prefix is used to specify a 32-bit register. The {...} syntax is for
+ // supporting different assembly dialects.
+ //
+ // What's basically happening is that we're saving and restoring the ebx register manually.
+ #if defined(DRFLAC_X86) && defined(__PIC__)
+ __asm__ __volatile__ (
+ "xchg{l} {%%}ebx, %k1;"
+ "cpuid;"
+ "xchg{l} {%%}ebx, %k1;"
+ : "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #else
+ __asm__ __volatile__ (
+ "cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
+ );
+ #endif
}
static MAL_INLINE unsigned long long mal_xgetbv(int reg)
@@ -2834,6 +2991,23 @@ static MAL_INLINE mal_bool32 mal_has_neon()
}
+static MAL_INLINE mal_bool32 mal_is_little_endian()
+{
+#if defined(MAL_X86) || defined(MAL_X64)
+ return MAL_TRUE;
+#else
+ int n = 1;
+ return (*(char*)&n) == 1;
+#endif
+}
+
+static MAL_INLINE mal_bool32 mal_is_big_endian()
+{
+ return !mal_is_little_endian();
+}
+
+
+
#ifndef MAL_PI
#define MAL_PI 3.14159265358979323846264f
#endif
@@ -2847,136 +3021,6 @@ static MAL_INLINE mal_bool32 mal_has_neon()
#define MAL_TAU_D 6.28318530717958647693
#endif
-// Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
-// using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
-// compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply
-// disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
-// not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
-//#define MAL_USE_RUNTIME_LINKING_FOR_PTHREAD
-
-// Disable run-time linking on certain backends.
-#ifndef MAL_NO_RUNTIME_LINKING
- #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN)
- #define MAL_NO_RUNTIME_LINKING
- #endif
-#endif
-
-// Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
-// certain unused functions and variables can be excluded from the build to avoid warnings.
-#ifdef MAL_ENABLE_WASAPI
- #define MAL_HAS_WASAPI // Every compiler should support WASAPI
-#endif
-#ifdef MAL_ENABLE_DSOUND
- #define MAL_HAS_DSOUND // Every compiler should support DirectSound.
-#endif
-#ifdef MAL_ENABLE_WINMM
- #define MAL_HAS_WINMM // Every compiler I'm aware of supports WinMM.
-#endif
-#ifdef MAL_ENABLE_ALSA
- #define MAL_HAS_ALSA
- #ifdef MAL_NO_RUNTIME_LINKING
- #ifdef __has_include
- #if !__has_include(<alsa/asoundlib.h>)
- #undef MAL_HAS_ALSA
- #endif
- #endif
- #endif
-#endif
-#ifdef MAL_ENABLE_PULSEAUDIO
- #define MAL_HAS_PULSEAUDIO // Development packages are unnecessary for PulseAudio.
- #ifdef MAL_NO_RUNTIME_LINKING
- #ifdef __has_include
- #if !__has_include(<pulse/pulseaudio.h>)
- #undef MAL_HAS_PULSEAUDIO
- #endif
- #endif
- #endif
-#endif
-#ifdef MAL_ENABLE_JACK
- #define MAL_HAS_JACK
- #ifdef MAL_NO_RUNTIME_LINKING
- #ifdef __has_include
- #if !__has_include(<jack/jack.h>)
- #undef MAL_HAS_JACK
- #endif
- #endif
- #endif
-#endif
-#ifdef MAL_ENABLE_COREAUDIO
- #define MAL_HAS_COREAUDIO
-#endif
-#ifdef MAL_ENABLE_OSS
- #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless.
-#endif
-#ifdef MAL_ENABLE_OPENSL
- #define MAL_HAS_OPENSL // OpenSL is the only supported backend for Android. It must be present.
-#endif
-#ifdef MAL_ENABLE_OPENAL
- #define MAL_HAS_OPENAL
- #ifdef MAL_NO_RUNTIME_LINKING
- #ifdef __has_include
- #if !__has_include(<AL/al.h>)
- #undef MAL_HAS_OPENAL
- #endif
- #endif
- #endif
-#endif
-#ifdef MAL_ENABLE_SDL
- #define MAL_HAS_SDL
-
- // SDL headers are necessary if using compile-time linking.
- #ifdef MAL_NO_RUNTIME_LINKING
- #ifdef __has_include
- #ifdef MAL_EMSCRIPTEN
- #if !__has_include(<SDL/SDL_audio.h>)
- #undef MAL_HAS_SDL
- #endif
- #else
- #if !__has_include(<SDL2/SDL_audio.h>)
- #undef MAL_HAS_SDL
- #endif
- #endif
- #endif
- #endif
-#endif
-#ifdef MAL_ENABLE_NULL
- #define MAL_HAS_NULL // Everything supports the null backend.
-#endif
-
-
-#ifdef MAL_WIN32
- #define MAL_THREADCALL WINAPI
- typedef unsigned long mal_thread_result;
-#else
- #define MAL_THREADCALL
- typedef void* mal_thread_result;
-#endif
-typedef mal_thread_result (MAL_THREADCALL * mal_thread_entry_proc)(void* pData);
-
-#ifdef MAL_WIN32
-typedef HRESULT (WINAPI * MAL_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
-typedef void (WINAPI * MAL_PFN_CoUninitialize)();
-typedef HRESULT (WINAPI * MAL_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
-typedef void (WINAPI * MAL_PFN_CoTaskMemFree)(LPVOID pv);
-typedef HRESULT (WINAPI * MAL_PFN_PropVariantClear)(PROPVARIANT *pvar);
-typedef int (WINAPI * MAL_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
-
-typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)();
-typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)();
-
-// Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG.
-typedef LONG (WINAPI * MAL_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
-typedef LONG (WINAPI * MAL_PFN_RegCloseKey)(HKEY hKey);
-typedef LONG (WINAPI * MAL_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
-#endif
-
-
-#define MAL_STATE_UNINITIALIZED 0
-#define MAL_STATE_STOPPED 1 // The device's default state after initialization.
-#define MAL_STATE_STARTED 2 // The worker thread is in it's main loop waiting for the driver to request or deliver audio data.
-#define MAL_STATE_STARTING 3 // Transitioning from a stopped state to started.
-#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped.
-
// The default format when mal_format_unknown (0) is requested when initializing a device.
#ifndef MAL_DEFAULT_FORMAT
@@ -3032,8 +3076,8 @@ mal_uint32 g_malStandardSampleRatePriorities[] = {
};
mal_format g_malFormatPriorities[] = {
- mal_format_f32, // Most common
- mal_format_s16,
+ mal_format_s16, // Most common
+ mal_format_f32,
//mal_format_s24_32, // Clean alignment
mal_format_s32,
@@ -3043,8 +3087,7 @@ mal_format g_malFormatPriorities[] = {
mal_format_u8 // Low quality
};
-#define MAL_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
-#define MAL_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
+
///////////////////////////////////////////////////////////////////////////////
//
@@ -3569,6 +3612,207 @@ void mal_split_buffer(void* pBuffer, size_t bufferSize, size_t splitCount, size_
#endif
+mal_uint32 mal_get_standard_sample_rate_priority_index(mal_uint32 sampleRate) // Lower = higher priority
+{
+ for (mal_uint32 i = 0; i < mal_countof(g_malStandardSampleRatePriorities); ++i) {
+ if (g_malStandardSampleRatePriorities[i] == sampleRate) {
+ return i;
+ }
+ }
+
+ return (mal_uint32)-1;
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// DEVICE I/O
+// ==========
+//
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+#ifndef MAL_NO_DEVICE_IO
+// Unfortunately using runtime linking for pthreads causes problems. This has occurred for me when testing on FreeBSD. When
+// using runtime linking, deadlocks can occur (for me it happens when loading data from fread()). It turns out that doing
+// compile-time linking fixes this. I'm not sure why this happens, but the safest way I can think of to fix this is to simply
+// disable runtime linking by default. To enable runtime linking, #define this before the implementation of this file. I am
+// not officially supporting this, but I'm leaving it here in case it's useful for somebody, somewhere.
+//#define MAL_USE_RUNTIME_LINKING_FOR_PTHREAD
+
+// Disable run-time linking on certain backends.
+#ifndef MAL_NO_RUNTIME_LINKING
+ #if defined(MAL_ANDROID) || defined(MAL_EMSCRIPTEN)
+ #define MAL_NO_RUNTIME_LINKING
+ #endif
+#endif
+
+// Check if we have the necessary development packages for each backend at the top so we can use this to determine whether or not
+// certain unused functions and variables can be excluded from the build to avoid warnings.
+#ifdef MAL_ENABLE_WASAPI
+ #define MAL_HAS_WASAPI // Every compiler should support WASAPI
+#endif
+#ifdef MAL_ENABLE_DSOUND
+ #define MAL_HAS_DSOUND // Every compiler should support DirectSound.
+#endif
+#ifdef MAL_ENABLE_WINMM
+ #define MAL_HAS_WINMM // Every compiler I'm aware of supports WinMM.
+#endif
+#ifdef MAL_ENABLE_ALSA
+ #define MAL_HAS_ALSA
+ #ifdef MAL_NO_RUNTIME_LINKING
+ #ifdef __has_include
+ #if !__has_include(<alsa/asoundlib.h>)
+ #undef MAL_HAS_ALSA
+ #endif
+ #endif
+ #endif
+#endif
+#ifdef MAL_ENABLE_PULSEAUDIO
+ #define MAL_HAS_PULSEAUDIO // Development packages are unnecessary for PulseAudio.
+ #ifdef MAL_NO_RUNTIME_LINKING
+ #ifdef __has_include
+ #if !__has_include(<pulse/pulseaudio.h>)
+ #undef MAL_HAS_PULSEAUDIO
+ #endif
+ #endif
+ #endif
+#endif
+#ifdef MAL_ENABLE_JACK
+ #define MAL_HAS_JACK
+ #ifdef MAL_NO_RUNTIME_LINKING
+ #ifdef __has_include
+ #if !__has_include(<jack/jack.h>)
+ #undef MAL_HAS_JACK
+ #endif
+ #endif
+ #endif
+#endif
+#ifdef MAL_ENABLE_COREAUDIO
+ #define MAL_HAS_COREAUDIO
+#endif
+#ifdef MAL_ENABLE_SNDIO
+ #define MAL_HAS_SNDIO
+#endif
+#ifdef MAL_ENABLE_AUDIOIO
+ #define MAL_HAS_AUDIOIO // When enabled, always assume audioio is available.
+#endif
+#ifdef MAL_ENABLE_OSS
+ #define MAL_HAS_OSS // OSS is the only supported backend for Unix and BSD, so it must be present else this library is useless.
+#endif
+#ifdef MAL_ENABLE_OPENSL
+ #define MAL_HAS_OPENSL // OpenSL is the only supported backend for Android. It must be present.
+#endif
+#ifdef MAL_ENABLE_OPENAL
+ #define MAL_HAS_OPENAL
+ #ifdef MAL_NO_RUNTIME_LINKING
+ #ifdef __has_include
+ #if !__has_include(<AL/al.h>)
+ #undef MAL_HAS_OPENAL
+ #endif
+ #endif
+ #endif
+#endif
+#ifdef MAL_ENABLE_SDL
+ #define MAL_HAS_SDL
+
+ // SDL headers are necessary if using compile-time linking.
+ #ifdef MAL_NO_RUNTIME_LINKING
+ #ifdef __has_include
+ #ifdef MAL_EMSCRIPTEN
+ #if !__has_include(<SDL/SDL_audio.h>)
+ #undef MAL_HAS_SDL
+ #endif
+ #else
+ #if !__has_include(<SDL2/SDL_audio.h>)
+ #undef MAL_HAS_SDL
+ #endif
+ #endif
+ #endif
+ #endif
+#endif
+#ifdef MAL_ENABLE_NULL
+ #define MAL_HAS_NULL // Everything supports the null backend.
+#endif
+
+const mal_backend g_malDefaultBackends[] = {
+ mal_backend_wasapi,
+ mal_backend_dsound,
+ mal_backend_winmm,
+ mal_backend_coreaudio,
+ mal_backend_sndio,
+ mal_backend_audioio,
+ mal_backend_oss,
+ mal_backend_pulseaudio,
+ mal_backend_alsa,
+ mal_backend_jack,
+ mal_backend_opensl,
+ mal_backend_openal,
+ mal_backend_sdl,
+ mal_backend_null
+};
+
+const char* mal_get_backend_name(mal_backend backend)
+{
+ switch (backend)
+ {
+ case mal_backend_null: return "Null";
+ case mal_backend_wasapi: return "WASAPI";
+ case mal_backend_dsound: return "DirectSound";
+ case mal_backend_winmm: return "WinMM";
+ case mal_backend_alsa: return "ALSA";
+ case mal_backend_pulseaudio: return "PulseAudio";
+ case mal_backend_jack: return "JACK";
+ case mal_backend_coreaudio: return "Core Audio";
+ case mal_backend_sndio: return "sndio";
+ case mal_backend_audioio: return "audioio";
+ case mal_backend_oss: return "OSS";
+ case mal_backend_opensl: return "OpenSL|ES";
+ case mal_backend_openal: return "OpenAL";
+ case mal_backend_sdl: return "SDL";
+ default: return "Unknown";
+ }
+}
+
+
+
+#ifdef MAL_WIN32
+ #define MAL_THREADCALL WINAPI
+ typedef unsigned long mal_thread_result;
+#else
+ #define MAL_THREADCALL
+ typedef void* mal_thread_result;
+#endif
+typedef mal_thread_result (MAL_THREADCALL * mal_thread_entry_proc)(void* pData);
+
+#ifdef MAL_WIN32
+typedef HRESULT (WINAPI * MAL_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit);
+typedef void (WINAPI * MAL_PFN_CoUninitialize)();
+typedef HRESULT (WINAPI * MAL_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
+typedef void (WINAPI * MAL_PFN_CoTaskMemFree)(LPVOID pv);
+typedef HRESULT (WINAPI * MAL_PFN_PropVariantClear)(PROPVARIANT *pvar);
+typedef int (WINAPI * MAL_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax);
+
+typedef HWND (WINAPI * MAL_PFN_GetForegroundWindow)();
+typedef HWND (WINAPI * MAL_PFN_GetDesktopWindow)();
+
+// Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG.
+typedef LONG (WINAPI * MAL_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult);
+typedef LONG (WINAPI * MAL_PFN_RegCloseKey)(HKEY hKey);
+typedef LONG (WINAPI * MAL_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData);
+#endif
+
+
+#define MAL_STATE_UNINITIALIZED 0
+#define MAL_STATE_STOPPED 1 // The device's default state after initialization.
+#define MAL_STATE_STARTED 2 // The worker thread is in it's main loop waiting for the driver to request or deliver audio data.
+#define MAL_STATE_STARTING 3 // Transitioning from a stopped state to started.
+#define MAL_STATE_STOPPING 4 // Transitioning from a started state to stopped.
+
+#define MAL_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
+#define MAL_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
+
+
///////////////////////////////////////////////////////////////////////////////
//
// Timing
@@ -4154,6 +4398,136 @@ mal_uint32 mal_get_closest_standard_sample_rate(mal_uint32 sampleRateIn)
}
+typedef struct
+{
+ mal_uint8* pInputFrames;
+ mal_uint32 framesRemaining;
+} mal_calculate_cpu_speed_factor_data;
+
+mal_uint32 mal_calculate_cpu_speed_factor__on_read(mal_dsp* pDSP, mal_uint32 framesToRead, void* pFramesOut, void* pUserData)
+{
+ mal_calculate_cpu_speed_factor_data* pData = (mal_calculate_cpu_speed_factor_data*)pUserData;
+ mal_assert(pData != NULL);
+
+ if (framesToRead > pData->framesRemaining) {
+ framesToRead = pData->framesRemaining;
+ }
+
+ mal_copy_memory(pFramesOut, pData->pInputFrames, framesToRead*pDSP->formatConverterIn.config.channels * sizeof(*pData->pInputFrames));
+
+ pData->pInputFrames += framesToRead;
+ pData->framesRemaining -= framesToRead;
+
+ return framesToRead;
+}
+
+float mal_calculate_cpu_speed_factor()
+{
+ // Our profiling test is based on how quick it can process 1 second worth of samples through mini_al's data conversion pipeline.
+
+ // This factor is multiplied with the profiling time. May need to fiddle with this to get an accurate value.
+ double f = 1000;
+
+ // Experiment: Reduce the factor a little when debug mode is used to reduce a blowout.
+#if !defined(NDEBUG) || defined(_DEBUG)
+ f /= 2;
+#endif
+
+ mal_uint32 sampleRateIn = 44100;
+ mal_uint32 sampleRateOut = 48000;
+ mal_uint32 channelsIn = 2;
+ mal_uint32 channelsOut = 6;
+
+ // Using the heap here to avoid an unnecessary static memory allocation. Also too big for the stack.
+ mal_uint8* pInputFrames = NULL;
+ float* pOutputFrames = NULL;
+
+ size_t inputDataSize = sampleRateIn * channelsIn * sizeof(*pInputFrames);
+ size_t outputDataSize = sampleRateOut * channelsOut * sizeof(*pOutputFrames);
+
+ void* pData = mal_malloc(inputDataSize + outputDataSize);
+ if (pData == NULL) {
+ return 1;
+ }
+
+ pInputFrames = (mal_uint8*)pData;
+ pOutputFrames = (float*)(pInputFrames + inputDataSize);
+
+
+
+
+ mal_calculate_cpu_speed_factor_data data;
+ data.pInputFrames = pInputFrames;
+ data.framesRemaining = sampleRateIn;
+
+ mal_dsp_config config = mal_dsp_config_init(mal_format_u8, channelsIn, sampleRateIn, mal_format_f32, channelsOut, sampleRateOut, mal_calculate_cpu_speed_factor__on_read, &data);
+
+ // Use linear sample rate conversion because it's the simplest and least likely to cause skewing as a result of tweaks to default
+ // configurations in the future.
+ config.srcAlgorithm = mal_src_algorithm_linear;
+
+ // Experiment: Disable SIMD extensions when profiling just to try and keep things a bit more consistent. The idea is to get a general
+ // indication on the speed of the system, but SIMD is used more heavily in the DSP pipeline than in the general case which may make
+ // the results a little less realistic.
+ config.noSSE2 = MAL_TRUE;
+ config.noAVX2 = MAL_TRUE;
+ config.noAVX512 = MAL_TRUE;
+ config.noNEON = MAL_TRUE;
+
+ mal_dsp dsp;
+ mal_result result = mal_dsp_init(&config, &dsp);
+ if (result != MAL_SUCCESS) {
+ mal_free(pData);
+ return 1;
+ }
+
+
+ int iterationCount = 2;
+
+ mal_timer timer;
+ mal_timer_init(&timer);
+ double startTime = mal_timer_get_time_in_seconds(&timer);
+ {
+ for (int i = 0; i < iterationCount; ++i) {
+ mal_dsp_read(&dsp, sampleRateOut, pOutputFrames, &data);
+ data.pInputFrames = pInputFrames;
+ data.framesRemaining = sampleRateIn;
+ }
+ }
+ double executionTimeInSeconds = mal_timer_get_time_in_seconds(&timer) - startTime;
+ executionTimeInSeconds /= iterationCount;
+
+
+ mal_free(pData);
+
+ // Guard against extreme blowouts.
+ return (float)mal_clamp(executionTimeInSeconds * f, 0.1, 100.0);
+}
+
+mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale)
+{
+ return mal_max(1, (mal_uint32)(baseBufferSize*scale));
+}
+
+mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale)
+{
+ mal_uint32 baseLatency;
+ if (performanceProfile == mal_performance_profile_low_latency) {
+ baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY;
+ } else {
+ baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE;
+ }
+
+ mal_uint32 sampleRateMS = (sampleRate/1000);
+
+ mal_uint32 minBufferSize = sampleRateMS * mal_min(baseLatency / 5, 1); // <-- Guard against multiply by zero.
+ mal_uint32 maxBufferSize = sampleRateMS * (baseLatency * 40);
+
+ mal_uint32 bufferSize = mal_scale_buffer_size((sampleRate/1000) * baseLatency, scale);
+ return mal_clamp(bufferSize, minBufferSize, maxBufferSize);
+}
+
+
const char* mal_log_level_to_string(mal_uint32 logLevel)
{
switch (logLevel)
@@ -6191,7 +6565,7 @@ mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice)
{
mal_assert(pDevice != NULL);
-#if 1
+#if 0
if (pDevice->type == mal_device_type_playback) {
mal_uint32 paddingFramesCount;
HRESULT hr = mal_IAudioClient_GetCurrentPadding((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount);
@@ -6215,15 +6589,20 @@ mal_uint32 mal_device__get_available_frames__wasapi(mal_device* pDevice)
}
#else
mal_uint32 paddingFramesCount;
- HRESULT hr = mal_IAudioClient_GetCurrentPadding(pDevice->wasapi.pAudioClient, &paddingFramesCount);
+ HRESULT hr = mal_IAudioClient_GetCurrentPadding((mal_IAudioClient*)pDevice->wasapi.pAudioClient, &paddingFramesCount);
if (FAILED(hr)) {
return 0;
}
+ // Slightly different rules for exclusive and shared modes.
if (pDevice->exclusiveMode) {
return paddingFramesCount;
} else {
- return pDevice->bufferSizeInFrames - paddingFramesCount;
+ if (pDevice->type == mal_device_type_playback) {
+ return pDevice->bufferSizeInFrames - paddingFramesCount;
+ } else {
+ return paddingFramesCount;
+ }
}
#endif
}
@@ -8843,16 +9222,6 @@ typedef size_t (* mal_snd_pcm_info_sizeof_proc)
typedef const char* (* mal_snd_pcm_info_get_name_proc) (const mal_snd_pcm_info_t* info);
typedef int (* mal_snd_config_update_free_global_proc) ();
-mal_snd_pcm_format_t g_mal_ALSAFormats[] = {
- MAL_SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown
- MAL_SND_PCM_FORMAT_U8, // mal_format_u8
- MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16
- MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24
- //MAL_SND_PCM_FORMAT_S24_LE, // mal_format_s24_32
- MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32
- MAL_SND_PCM_FORMAT_FLOAT_LE // mal_format_f32
-};
-
// This array specifies each of the common devices that can be used for both playback and capture.
const char* g_malCommonDeviceNamesALSA[] = {
"default",
@@ -8900,20 +9269,52 @@ float mal_find_default_buffer_size_scale__alsa(const char* deviceName)
mal_snd_pcm_format_t mal_convert_mal_format_to_alsa_format(mal_format format)
{
- return g_mal_ALSAFormats[format];
+ mal_snd_pcm_format_t ALSAFormats[] = {
+ MAL_SND_PCM_FORMAT_UNKNOWN, // mal_format_unknown
+ MAL_SND_PCM_FORMAT_U8, // mal_format_u8
+ MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16
+ MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24
+ MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32
+ MAL_SND_PCM_FORMAT_FLOAT_LE // mal_format_f32
+ };
+
+ if (mal_is_big_endian()) {
+ ALSAFormats[0] = MAL_SND_PCM_FORMAT_UNKNOWN;
+ ALSAFormats[1] = MAL_SND_PCM_FORMAT_U8;
+ ALSAFormats[2] = MAL_SND_PCM_FORMAT_S16_BE;
+ ALSAFormats[3] = MAL_SND_PCM_FORMAT_S24_3BE;
+ ALSAFormats[4] = MAL_SND_PCM_FORMAT_S32_BE;
+ ALSAFormats[5] = MAL_SND_PCM_FORMAT_FLOAT_BE;
+ }
+
+
+ return ALSAFormats[format];
}
mal_format mal_convert_alsa_format_to_mal_format(mal_snd_pcm_format_t formatALSA)
{
- switch (formatALSA)
- {
- case MAL_SND_PCM_FORMAT_U8: return mal_format_u8;
- case MAL_SND_PCM_FORMAT_S16_LE: return mal_format_s16;
- case MAL_SND_PCM_FORMAT_S24_3LE: return mal_format_s24;
- //case MAL_SND_PCM_FORMAT_S24_LE: return mal_format_s24_32
- case MAL_SND_PCM_FORMAT_S32_LE: return mal_format_s32;
- case MAL_SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32;
- default: return mal_format_unknown;
+ if (mal_is_little_endian()) {
+ switch (formatALSA) {
+ case MAL_SND_PCM_FORMAT_S16_LE: return mal_format_s16;
+ case MAL_SND_PCM_FORMAT_S24_3LE: return mal_format_s24;
+ case MAL_SND_PCM_FORMAT_S32_LE: return mal_format_s32;
+ case MAL_SND_PCM_FORMAT_FLOAT_LE: return mal_format_f32;
+ default: break;
+ }
+ } else {
+ switch (formatALSA) {
+ case MAL_SND_PCM_FORMAT_S16_BE: return mal_format_s16;
+ case MAL_SND_PCM_FORMAT_S24_3BE: return mal_format_s24;
+ case MAL_SND_PCM_FORMAT_S32_BE: return mal_format_s32;
+ case MAL_SND_PCM_FORMAT_FLOAT_BE: return mal_format_f32;
+ default: break;
+ }
+ }
+
+ // Endian agnostic.
+ switch (formatALSA) {
+ case MAL_SND_PCM_FORMAT_U8: return mal_format_u8;
+ default: return mal_format_unknown;
}
}
@@ -9933,11 +10334,19 @@ mal_result mal_device_init__alsa(mal_context* pContext, mal_device_type type, co
MAL_SND_PCM_FORMAT_FLOAT_LE, // mal_format_f32
MAL_SND_PCM_FORMAT_S32_LE, // mal_format_s32
MAL_SND_PCM_FORMAT_S24_3LE, // mal_format_s24
- //MAL_SND_PCM_FORMAT_S24_LE, // mal_format_s24_32
MAL_SND_PCM_FORMAT_S16_LE, // mal_format_s16
MAL_SND_PCM_FORMAT_U8 // mal_format_u8
};
+ if (mal_is_big_endian()) {
+ preferredFormatsALSA[0] = MAL_SND_PCM_FORMAT_FLOAT_BE;
+ preferredFormatsALSA[1] = MAL_SND_PCM_FORMAT_S32_BE;
+ preferredFormatsALSA[2] = MAL_SND_PCM_FORMAT_S24_3BE;
+ preferredFormatsALSA[3] = MAL_SND_PCM_FORMAT_S16_BE;
+ preferredFormatsALSA[4] = MAL_SND_PCM_FORMAT_U8;
+ }
+
+
formatALSA = MAL_SND_PCM_FORMAT_UNKNOWN;
for (size_t i = 0; i < (sizeof(preferredFormatsALSA) / sizeof(preferredFormatsALSA[0])); ++i) {
if (((mal_snd_pcm_format_mask_test_proc)pContext->alsa.snd_pcm_format_mask_test)(pFormatMask, preferredFormatsALSA[i])) {
@@ -10943,20 +11352,27 @@ mal_result mal_result_from_pulse(int result)
#if 0
mal_pa_sample_format_t mal_format_to_pulse(mal_format format)
{
- switch (format)
- {
- case mal_format_u8: return MAL_PA_SAMPLE_U8;
- case mal_format_s16: return MAL_PA_SAMPLE_S16LE;
- //case mal_format_s16be: return MAL_PA_SAMPLE_S16BE;
- case mal_format_s24: return MAL_PA_SAMPLE_S24LE;
- //case mal_format_s24be: return MAL_PA_SAMPLE_S24BE;
- //case mal_format_s24_32: return MAL_PA_SAMPLE_S24_32LE;
- //case mal_format_s24_32be: return MAL_PA_SAMPLE_S24_32BE;
- case mal_format_s32: return MAL_PA_SAMPLE_S32LE;
- //case mal_format_s32be: return MAL_PA_SAMPLE_S32BE;
- case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32LE;
- //case mal_format_f32be: return PA_SAMPLE_FLOAT32BE;
+ if (mal_is_little_endian()) {
+ switch (format) {
+ case mal_format_s16: return MAL_PA_SAMPLE_S16LE;
+ case mal_format_s24: return MAL_PA_SAMPLE_S24LE;
+ case mal_format_s32: return MAL_PA_SAMPLE_S32LE;
+ case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32LE;
+ default: break;
+ }
+ } else {
+ switch (format) {
+ case mal_format_s16: return MAL_PA_SAMPLE_S16BE;
+ case mal_format_s24: return MAL_PA_SAMPLE_S24BE;
+ case mal_format_s32: return MAL_PA_SAMPLE_S32BE;
+ case mal_format_f32: return MAL_PA_SAMPLE_FLOAT32BE;
+ default: break;
+ }
+ }
+ // Endian agnostic.
+ switch (format) {
+ case mal_format_u8: return MAL_PA_SAMPLE_U8;
default: return MAL_PA_SAMPLE_INVALID;
}
}
@@ -10964,20 +11380,27 @@ mal_pa_sample_format_t mal_format_to_pulse(mal_format format)
mal_format mal_format_from_pulse(mal_pa_sample_format_t format)
{
- switch (format)
- {
- case MAL_PA_SAMPLE_U8: return mal_format_u8;
- case MAL_PA_SAMPLE_S16LE: return mal_format_s16;
- //case MAL_PA_SAMPLE_S16BE: return mal_format_s16be;
- case MAL_PA_SAMPLE_S24LE: return mal_format_s24;
- //case MAL_PA_SAMPLE_S24BE: return mal_format_s24be;
- //case MAL_PA_SAMPLE_S24_32LE: return mal_format_s24_32;
- //case MAL_PA_SAMPLE_S24_32BE: return mal_format_s24_32be;
- case MAL_PA_SAMPLE_S32LE: return mal_format_s32;
- //case MAL_PA_SAMPLE_S32BE: return mal_format_s32be;
- case MAL_PA_SAMPLE_FLOAT32LE: return mal_format_f32;
- //case MAL_PA_SAMPLE_FLOAT32BE: return mal_format_f32be;
+ if (mal_is_little_endian()) {
+ switch (format) {
+ case MAL_PA_SAMPLE_S16LE: return mal_format_s16;
+ case MAL_PA_SAMPLE_S24LE: return mal_format_s24;
+ case MAL_PA_SAMPLE_S32LE: return mal_format_s32;
+ case MAL_PA_SAMPLE_FLOAT32LE: return mal_format_f32;
+ default: break;
+ }
+ } else {
+ switch (format) {
+ case MAL_PA_SAMPLE_S16BE: return mal_format_s16;
+ case MAL_PA_SAMPLE_S24BE: return mal_format_s24;
+ case MAL_PA_SAMPLE_S32BE: return mal_format_s32;
+ case MAL_PA_SAMPLE_FLOAT32BE: return mal_format_f32;
+ default: break;
+ }
+ }
+ // Endian agnostic.
+ switch (format) {
+ case MAL_PA_SAMPLE_U8: return mal_format_u8;
default: return mal_format_unknown;
}
}
@@ -12967,9 +13390,9 @@ mal_result mal_format_from_AudioStreamBasicDescription(const AudioStreamBasicDes
if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
return MAL_FORMAT_NOT_SUPPORTED;
}
-
- // Big-endian formats are not currently supported, but will be added in a future version of mini_al.
- if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
+
+ // Only supporting native-endian.
+ if ((mal_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (mal_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
return MAL_FORMAT_NOT_SUPPORTED;
}
@@ -14567,6 +14990,1299 @@ mal_result mal_context_init__coreaudio(mal_context* pContext)
///////////////////////////////////////////////////////////////////////////////
//
+// sndio Backend
+//
+///////////////////////////////////////////////////////////////////////////////
+#ifdef MAL_HAS_SNDIO
+#include <fcntl.h>
+#include <sys/stat.h>
+
+// Only supporting OpenBSD. This did not work very well at all on FreeBSD when I tried it. Not sure if this is due
+// to mini_al's implementation or if it's some kind of system configuration issue, but basically the default device
+// just doesn't emit any sound, or at times you'll hear tiny pieces. I will consider enabling this when there's
+// demand for it or if I can get it tested and debugged more thoroughly.
+
+//#if defined(__NetBSD__) || defined(__OpenBSD__)
+//#include <sys/audioio.h>
+//#endif
+//#if defined(__FreeBSD__) || defined(__DragonFly__)
+//#include <sys/soundcard.h>
+//#endif
+
+#define MAL_SIO_DEVANY "default"
+#define MAL_SIO_PLAY 1
+#define MAL_SIO_REC 2
+#define MAL_SIO_NENC 8
+#define MAL_SIO_NCHAN 8
+#define MAL_SIO_NRATE 16
+#define MAL_SIO_NCONF 4
+
+struct mal_sio_hdl; // <-- Opaque
+
+struct mal_sio_par
+{
+ unsigned int bits;
+ unsigned int bps;
+ unsigned int sig;
+ unsigned int le;
+ unsigned int msb;
+ unsigned int rchan;
+ unsigned int pchan;
+ unsigned int rate;
+ unsigned int bufsz;
+ unsigned int xrun;
+ unsigned int round;
+ unsigned int appbufsz;
+ int __pad[3];
+ unsigned int __magic;
+};
+
+struct mal_sio_enc
+{
+ unsigned int bits;
+ unsigned int bps;
+ unsigned int sig;
+ unsigned int le;
+ unsigned int msb;
+};
+
+struct mal_sio_conf
+{
+ unsigned int enc;
+ unsigned int rchan;
+ unsigned int pchan;
+ unsigned int rate;
+};
+
+struct mal_sio_cap
+{
+ struct mal_sio_enc enc[MAL_SIO_NENC];
+ unsigned int rchan[MAL_SIO_NCHAN];
+ unsigned int pchan[MAL_SIO_NCHAN];
+ unsigned int rate[MAL_SIO_NRATE];
+ int __pad[7];
+ unsigned int nconf;
+ struct mal_sio_conf confs[MAL_SIO_NCONF];
+};
+
+typedef struct mal_sio_hdl* (* mal_sio_open_proc) (const char*, unsigned int, int);
+typedef void (* mal_sio_close_proc) (struct mal_sio_hdl*);
+typedef int (* mal_sio_setpar_proc) (struct mal_sio_hdl*, struct mal_sio_par*);
+typedef int (* mal_sio_getpar_proc) (struct mal_sio_hdl*, struct mal_sio_par*);
+typedef int (* mal_sio_getcap_proc) (struct mal_sio_hdl*, struct mal_sio_cap*);
+typedef size_t (* mal_sio_write_proc) (struct mal_sio_hdl*, const void*, size_t);
+typedef size_t (* mal_sio_read_proc) (struct mal_sio_hdl*, void*, size_t);
+typedef int (* mal_sio_start_proc) (struct mal_sio_hdl*);
+typedef int (* mal_sio_stop_proc) (struct mal_sio_hdl*);
+typedef int (* mal_sio_initpar_proc)(struct mal_sio_par*);
+
+mal_format mal_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
+{
+ // We only support native-endian right now.
+ if ((mal_is_little_endian() && le == 0) || (mal_is_big_endian() && le == 1)) {
+ return mal_format_unknown;
+ }
+
+ if (bits == 8 && bps == 1 && sig == 0) {
+ return mal_format_u8;
+ }
+ if (bits == 16 && bps == 2 && sig == 1) {
+ return mal_format_s16;
+ }
+ if (bits == 24 && bps == 3 && sig == 1) {
+ return mal_format_s24;
+ }
+ if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
+ //return mal_format_s24_32;
+ }
+ if (bits == 32 && bps == 4 && sig == 1) {
+ return mal_format_s32;
+ }
+
+ return mal_format_unknown;
+}
+
+mal_format mal_find_best_format_from_sio_cap__sndio(struct mal_sio_cap* caps)
+{
+ mal_assert(caps != NULL);
+
+ mal_format bestFormat = mal_format_unknown;
+ for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
+ for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) {
+ if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
+ continue;
+ }
+
+ unsigned int bits = caps->enc[iEncoding].bits;
+ unsigned int bps = caps->enc[iEncoding].bps;
+ unsigned int sig = caps->enc[iEncoding].sig;
+ unsigned int le = caps->enc[iEncoding].le;
+ unsigned int msb = caps->enc[iEncoding].msb;
+ mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
+ if (format == mal_format_unknown) {
+ continue; // Format not supported.
+ }
+
+ if (bestFormat == mal_format_unknown) {
+ bestFormat = format;
+ } else {
+ if (mal_get_format_priority_index(bestFormat) > mal_get_format_priority_index(format)) { // <-- Lower = better.
+ bestFormat = format;
+ }
+ }
+ }
+ }
+
+ return mal_format_unknown;
+}
+
+mal_uint32 mal_find_best_channels_from_sio_cap__sndio(struct mal_sio_cap* caps, mal_device_type deviceType, mal_format requiredFormat)
+{
+ mal_assert(caps != NULL);
+ mal_assert(requiredFormat != mal_format_unknown);
+
+ // Just pick whatever configuration has the most channels.
+ mal_uint32 maxChannels = 0;
+ for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
+ // The encoding should be of requiredFormat.
+ for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) {
+ if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
+ continue;
+ }
+
+ unsigned int bits = caps->enc[iEncoding].bits;
+ unsigned int bps = caps->enc[iEncoding].bps;
+ unsigned int sig = caps->enc[iEncoding].sig;
+ unsigned int le = caps->enc[iEncoding].le;
+ unsigned int msb = caps->enc[iEncoding].msb;
+ mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
+ if (format != requiredFormat) {
+ continue;
+ }
+
+ // Getting here means the format is supported. Iterate over each channel count and grab the biggest one.
+ for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) {
+ unsigned int chan = 0;
+ if (deviceType == mal_device_type_playback) {
+ chan = caps->confs[iConfig].pchan;
+ } else {
+ chan = caps->confs[iConfig].rchan;
+ }
+
+ if ((chan & (1UL << iChannel)) == 0) {
+ continue;
+ }
+
+ unsigned int channels;
+ if (deviceType == mal_device_type_playback) {
+ channels = caps->pchan[iChannel];
+ } else {
+ channels = caps->rchan[iChannel];
+ }
+
+ if (maxChannels < channels) {
+ maxChannels = channels;
+ }
+ }
+ }
+ }
+
+ return maxChannels;
+}
+
+mal_uint32 mal_find_best_sample_rate_from_sio_cap__sndio(struct mal_sio_cap* caps, mal_device_type deviceType, mal_format requiredFormat, mal_uint32 requiredChannels)
+{
+ mal_assert(caps != NULL);
+ mal_assert(requiredFormat != mal_format_unknown);
+ mal_assert(requiredChannels > 0);
+ mal_assert(requiredChannels <= MAL_MAX_CHANNELS);
+
+ mal_uint32 firstSampleRate = 0; // <-- If the device does not support a standard rate we'll fall back to the first one that's found.
+
+ mal_uint32 bestSampleRate = 0;
+ for (unsigned int iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
+ // The encoding should be of requiredFormat.
+ for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) {
+ if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
+ continue;
+ }
+
+ unsigned int bits = caps->enc[iEncoding].bits;
+ unsigned int bps = caps->enc[iEncoding].bps;
+ unsigned int sig = caps->enc[iEncoding].sig;
+ unsigned int le = caps->enc[iEncoding].le;
+ unsigned int msb = caps->enc[iEncoding].msb;
+ mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
+ if (format != requiredFormat) {
+ continue;
+ }
+
+ // Getting here means the format is supported. Iterate over each channel count and grab the biggest one.
+ for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) {
+ unsigned int chan = 0;
+ if (deviceType == mal_device_type_playback) {
+ chan = caps->confs[iConfig].pchan;
+ } else {
+ chan = caps->confs[iConfig].rchan;
+ }
+
+ if ((chan & (1UL << iChannel)) == 0) {
+ continue;
+ }
+
+ unsigned int channels;
+ if (deviceType == mal_device_type_playback) {
+ channels = caps->pchan[iChannel];
+ } else {
+ channels = caps->rchan[iChannel];
+ }
+
+ if (channels != requiredChannels) {
+ continue;
+ }
+
+ // Getting here means we have found a compatible encoding/channel pair.
+ for (unsigned int iRate = 0; iRate < MAL_SIO_NRATE; iRate += 1) {
+ mal_uint32 rate = (mal_uint32)caps->rate[iRate];
+
+ if (firstSampleRate == 0) {
+ firstSampleRate = rate;
+ }
+
+ // Disregard this rate if it's not a standard one.
+ mal_uint32 ratePriority = mal_get_standard_sample_rate_priority_index(rate);
+ if (ratePriority == (mal_uint32)-1) {
+ continue;
+ }
+
+ if (mal_get_standard_sample_rate_priority_index(bestSampleRate) > ratePriority) { // Lower = better.
+ bestSampleRate = rate;
+ }
+ }
+ }
+ }
+ }
+
+ // If a standard sample rate was not found just fall back to the first one that was iterated.
+ if (bestSampleRate == 0) {
+ bestSampleRate = firstSampleRate;
+ }
+
+ return bestSampleRate;
+}
+
+
+mal_bool32 mal_context_is_device_id_equal__sndio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pID0 != NULL);
+ mal_assert(pID1 != NULL);
+ (void)pContext;
+
+ return mal_strcmp(pID0->sndio, pID1->sndio) == 0;
+}
+
+mal_result mal_context_enumerate_devices__sndio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(callback != NULL);
+
+ // sndio doesn't seem to have a good device enumeration API, so I'm therefore only enumerating
+ // over default devices for now.
+ mal_bool32 isTerminating = MAL_FALSE;
+ struct mal_sio_hdl* handle;
+
+ // Playback.
+ if (!isTerminating) {
+ handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(MAL_SIO_DEVANY, MAL_SIO_PLAY, 0);
+ if (handle != NULL) {
+ // Supports playback.
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ mal_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MAL_SIO_DEVANY);
+ mal_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_PLAYBACK_DEVICE_NAME);
+
+ isTerminating = !callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
+
+ ((mal_sio_close_proc)pContext->sndio.sio_close)(handle);
+ }
+ }
+
+ // Capture.
+ if (!isTerminating) {
+ handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(MAL_SIO_DEVANY, MAL_SIO_REC, 0);
+ if (handle != NULL) {
+ // Supports capture.
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ mal_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
+ mal_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MAL_DEFAULT_CAPTURE_DEVICE_NAME);
+
+ isTerminating = !callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
+
+ ((mal_sio_close_proc)pContext->sndio.sio_close)(handle);
+ }
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_get_device_info__sndio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo)
+{
+ mal_assert(pContext != NULL);
+ (void)shareMode;
+
+ // We need to open the device before we can get information about it.
+ char devid[256];
+ if (pDeviceID == NULL) {
+ mal_strcpy_s(devid, sizeof(devid), MAL_SIO_DEVANY);
+ mal_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == mal_device_type_playback) ? MAL_DEFAULT_PLAYBACK_DEVICE_NAME : MAL_DEFAULT_CAPTURE_DEVICE_NAME);
+ } else {
+ mal_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
+ mal_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
+ }
+
+ struct mal_sio_hdl* handle = ((mal_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == mal_device_type_playback) ? MAL_SIO_PLAY : MAL_SIO_REC, 0);
+ if (handle == NULL) {
+ return MAL_NO_DEVICE;
+ }
+
+ struct mal_sio_cap caps;
+ if (((mal_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
+ return MAL_ERROR;
+ }
+
+ for (unsigned int iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
+ // The main thing we care about is that the encoding is supported by mini_al. If it is, we want to give
+ // preference to some formats over others.
+ for (unsigned int iEncoding = 0; iEncoding < MAL_SIO_NENC; iEncoding += 1) {
+ if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
+ continue;
+ }
+
+ unsigned int bits = caps.enc[iEncoding].bits;
+ unsigned int bps = caps.enc[iEncoding].bps;
+ unsigned int sig = caps.enc[iEncoding].sig;
+ unsigned int le = caps.enc[iEncoding].le;
+ unsigned int msb = caps.enc[iEncoding].msb;
+ mal_format format = mal_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
+ if (format == mal_format_unknown) {
+ continue; // Format not supported.
+ }
+
+ // Add this format if it doesn't already exist.
+ mal_bool32 formatExists = MAL_FALSE;
+ for (mal_uint32 iExistingFormat = 0; iExistingFormat < pDeviceInfo->formatCount; iExistingFormat += 1) {
+ if (pDeviceInfo->formats[iExistingFormat] == format) {
+ formatExists = MAL_TRUE;
+ break;
+ }
+ }
+
+ if (!formatExists) {
+ pDeviceInfo->formats[pDeviceInfo->formatCount++] = format;
+ }
+ }
+
+ // Channels.
+ for (unsigned int iChannel = 0; iChannel < MAL_SIO_NCHAN; iChannel += 1) {
+ unsigned int chan = 0;
+ if (deviceType == mal_device_type_playback) {
+ chan = caps.confs[iConfig].pchan;
+ } else {
+ chan = caps.confs[iConfig].rchan;
+ }
+
+ if ((chan & (1UL << iChannel)) == 0) {
+ continue;
+ }
+
+ unsigned int channels;
+ if (deviceType == mal_device_type_playback) {
+ channels = caps.pchan[iChannel];
+ } else {
+ channels = caps.rchan[iChannel];
+ }
+
+ if (pDeviceInfo->minChannels > channels) {
+ pDeviceInfo->minChannels = channels;
+ }
+ if (pDeviceInfo->maxChannels < channels) {
+ pDeviceInfo->maxChannels = channels;
+ }
+ }
+
+ // Sample rates.
+ for (unsigned int iRate = 0; iRate < MAL_SIO_NRATE; iRate += 1) {
+ if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
+ unsigned int rate = caps.rate[iRate];
+ if (pDeviceInfo->minSampleRate > rate) {
+ pDeviceInfo->minSampleRate = rate;
+ }
+ if (pDeviceInfo->maxSampleRate < rate) {
+ pDeviceInfo->maxSampleRate = rate;
+ }
+ }
+ }
+ }
+
+ ((mal_sio_close_proc)pContext->sndio.sio_close)(handle);
+ return MAL_SUCCESS;
+}
+
+void mal_device_uninit__sndio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ ((mal_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle);
+ mal_free(pDevice->sndio.pIntermediaryBuffer);
+}
+
+mal_result mal_device_init__sndio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
+{
+ (void)pContext;
+
+ mal_assert(pDevice != NULL);
+ mal_zero_object(&pDevice->sndio);
+
+ const char* deviceName = MAL_SIO_DEVANY;
+//#if defined(__FreeBSD__) || defined(__DragonFly__)
+// deviceName = "rsnd/0";
+//#else
+ if (pDeviceID != NULL) {
+ deviceName = pDeviceID->sndio;
+ }
+
+ pDevice->sndio.handle = (mal_ptr)((mal_sio_open_proc)pContext->sndio.sio_open)(deviceName, (deviceType == mal_device_type_playback) ? MAL_SIO_PLAY : MAL_SIO_REC, 0);
+ if (pDevice->sndio.handle == NULL) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+ }
+
+ // We need to retrieve the device caps to determine the most appropriate format to use.
+ struct mal_sio_cap caps;
+ if (((mal_sio_getcap_proc)pContext->sndio.sio_getcap)((struct mal_sio_hdl*)pDevice->sndio.handle, &caps) == 0) {
+ ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.", MAL_ERROR);
+ }
+
+ mal_format desiredFormat = pDevice->format;
+ if (pDevice->usingDefaultFormat) {
+ desiredFormat = mal_find_best_format_from_sio_cap__sndio(&caps);
+ }
+
+ if (desiredFormat == mal_format_unknown) {
+ desiredFormat = pDevice->format;
+ }
+
+
+ // Note: sndio reports a huge range of available channels. This is inconvenient for us because there's no real
+ // way, as far as I can tell, to get the _actual_ channel count of the device. I'm therefore restricting this
+ // to the requested channels, regardless of whether or not the default channel count is requested.
+ //
+ // For hardware devices, I'm suspecting only a single channel count will be reported and we can safely use the
+ // value returned by mal_find_best_channels_from_sio_cap__sndio().
+ mal_uint32 desiredChannels = pDevice->channels;
+ if (pDevice->usingDefaultChannels) {
+ if (strlen(deviceName) > strlen("rsnd/") && strncmp(deviceName, "rsnd/", strlen("rsnd/")) == 0) {
+ desiredChannels = mal_find_best_channels_from_sio_cap__sndio(&caps, deviceType, desiredFormat);
+ }
+ }
+
+ if (desiredChannels == 0) {
+ desiredChannels = pDevice->channels;
+ }
+
+
+ mal_uint32 desiredSampleRate = pDevice->sampleRate;
+ if (pDevice->usingDefaultSampleRate) {
+ desiredSampleRate = mal_find_best_sample_rate_from_sio_cap__sndio(&caps, deviceType, desiredFormat, desiredChannels);
+ }
+
+ if (desiredSampleRate == 0) {
+ desiredSampleRate = pDevice->sampleRate;
+ }
+
+
+ struct mal_sio_par par;
+ ((mal_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
+ par.msb = 0;
+ par.le = mal_is_little_endian();
+
+ switch (desiredFormat) {
+ case mal_format_u8:
+ {
+ par.bits = 8;
+ par.bps = 1;
+ par.sig = 0;
+ } break;
+
+ case mal_format_s24:
+ {
+ par.bits = 24;
+ par.bps = 3;
+ par.sig = 1;
+ } break;
+
+ case mal_format_s32:
+ {
+ par.bits = 32;
+ par.bps = 4;
+ par.sig = 1;
+ } break;
+
+ case mal_format_s16:
+ case mal_format_f32:
+ default:
+ {
+ par.bits = 16;
+ par.bps = 2;
+ par.sig = 1;
+ } break;
+ }
+
+ if (deviceType == mal_device_type_playback) {
+ par.pchan = desiredChannels;
+ } else {
+ par.rchan = desiredChannels;
+ }
+
+ par.rate = desiredSampleRate;
+
+ // Try calculating an appropriate default buffer size after we have the sample rate.
+ mal_uint32 desiredBufferSizeInFrames = pDevice->bufferSizeInFrames;
+ if (pDevice->usingDefaultBufferSize) {
+ // CPU speed factor.
+ float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+ // Playback vs capture latency.
+ float fDeviceType = 1;
+
+ // Backend tax.
+ float fBackend = 1;
+
+ desiredBufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, par.rate, fCPUSpeed*fDeviceType*fBackend);
+ }
+
+ par.round = desiredBufferSizeInFrames / pDevice->periods;
+ par.appbufsz = par.round * pDevice->periods;
+
+ if (((mal_sio_setpar_proc)pContext->sndio.sio_setpar)((struct mal_sio_hdl*)pDevice->sndio.handle, &par) == 0) {
+ ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+ if (((mal_sio_getpar_proc)pContext->sndio.sio_getpar)((struct mal_sio_hdl*)pDevice->sndio.handle, &par) == 0) {
+ ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+
+ pDevice->internalFormat = mal_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
+
+ if (deviceType == mal_device_type_playback) {
+ pDevice->internalChannels = par.pchan;
+ } else {
+ pDevice->internalChannels = par.rchan;
+ }
+
+ pDevice->internalSampleRate = par.rate;
+
+ pDevice->periods = par.appbufsz / par.round;
+ if (pDevice->periods < 2) {
+ pDevice->periods = 2;
+ }
+ pDevice->bufferSizeInFrames = par.round * pDevice->periods;
+ pDevice->sndio.fragmentSizeInFrames = par.round;
+
+ mal_get_standard_channel_map(mal_standard_channel_map_sndio, pDevice->internalChannels, pDevice->internalChannelMap);
+
+ pDevice->sndio.pIntermediaryBuffer = mal_malloc(pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels));
+ if (pDevice->sndio.pIntermediaryBuffer == NULL) {
+ ((mal_sio_close_proc)pContext->sndio.sio_close)((struct mal_sio_hdl*)pDevice->sndio.handle);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY);
+ }
+
+#ifdef MAL_DEBUG_OUTPUT
+ printf("DEVICE INFO\n");
+ printf(" Format: %s\n", mal_get_format_name(pDevice->internalFormat));
+ printf(" Channels: %d\n", pDevice->internalChannels);
+ printf(" Sample Rate: %d\n", pDevice->internalSampleRate);
+ printf(" Buffer Size: %d\n", pDevice->bufferSizeInFrames);
+ printf(" Periods: %d\n", pDevice->periods);
+ printf(" appbufsz: %d\n", par.appbufsz);
+ printf(" round: %d\n", par.round);
+#endif
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__start_backend__sndio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ if (((mal_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct mal_sio_hdl*)pDevice->sndio.handle) == 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to start backend device.", MAL_FAILED_TO_START_BACKEND_DEVICE);
+ }
+
+ // The device is started by the next calls to read() and write(). For playback it's simple - just read
+ // data from the client, then write it to the device with write() which will in turn start the device.
+ // For capture it's a bit less intuitive - we do nothing (it'll be started automatically by the first
+ // call to read().
+ if (pDevice->type == mal_device_type_playback) {
+ // Playback. Need to load the entire buffer, which means we need to write a fragment for each period.
+ for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; iPeriod += 1) {
+ mal_device__read_frames_from_client(pDevice, pDevice->sndio.fragmentSizeInFrames, pDevice->sndio.pIntermediaryBuffer);
+
+ int bytesWritten = ((mal_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels));
+ if (bytesWritten == 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE);
+ }
+ }
+ } else {
+ // Capture. Do nothing.
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__stop_backend__sndio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ ((mal_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct mal_sio_hdl*)pDevice->sndio.handle);
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__break_main_loop__sndio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ pDevice->sndio.breakFromMainLoop = MAL_TRUE;
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__main_loop__sndio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ pDevice->sndio.breakFromMainLoop = MAL_FALSE;
+ while (!pDevice->sndio.breakFromMainLoop) {
+ // Break from the main loop if the device isn't started anymore. Likely what's happened is the application
+ // has requested that the device be stopped.
+ if (!mal_device_is_started(pDevice)) {
+ break;
+ }
+
+ if (pDevice->type == mal_device_type_playback) {
+ // Playback.
+ mal_device__read_frames_from_client(pDevice, pDevice->sndio.fragmentSizeInFrames, pDevice->sndio.pIntermediaryBuffer);
+
+ int bytesWritten = ((mal_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat));
+ if (bytesWritten == 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE);
+ }
+ } else {
+ // Capture.
+ int bytesRead = ((mal_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct mal_sio_hdl*)pDevice->sndio.handle, pDevice->sndio.pIntermediaryBuffer, pDevice->sndio.fragmentSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat));
+ if (bytesRead == 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE);
+ }
+
+ mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_bytes_per_sample(pDevice->internalFormat);
+ mal_device__send_frames_to_client(pDevice, framesRead, pDevice->sndio.pIntermediaryBuffer);
+ }
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_uninit__sndio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pContext->backend == mal_backend_sndio);
+
+ (void)pContext;
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_init__sndio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+
+#ifndef MAL_NO_RUNTIME_LINKING
+ // libpulse.so
+ const char* libsndioNames[] = {
+ "libsndio.so"
+ };
+
+ for (size_t i = 0; i < mal_countof(libsndioNames); ++i) {
+ pContext->sndio.sndioSO = mal_dlopen(libsndioNames[i]);
+ if (pContext->sndio.sndioSO != NULL) {
+ break;
+ }
+ }
+
+ if (pContext->sndio.sndioSO == NULL) {
+ return MAL_NO_BACKEND;
+ }
+
+ pContext->sndio.sio_open = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_open");
+ pContext->sndio.sio_close = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_close");
+ pContext->sndio.sio_setpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_setpar");
+ pContext->sndio.sio_getpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_getpar");
+ pContext->sndio.sio_getcap = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_getcap");
+ pContext->sndio.sio_write = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_write");
+ pContext->sndio.sio_read = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_read");
+ pContext->sndio.sio_start = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_start");
+ pContext->sndio.sio_stop = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_stop");
+ pContext->sndio.sio_initpar = (mal_proc)mal_dlsym(pContext->sndio.sndioSO, "sio_initpar");
+#else
+ pContext->sndio.sio_open = sio_open;
+ pContext->sndio.sio_close = sio_close;
+ pContext->sndio.sio_setpar = sio_setpar;
+ pContext->sndio.sio_getpar = sio_getpar;
+ pContext->sndio.sio_getcap = sio_getcap;
+ pContext->sndio.sio_write = sio_write;
+ pContext->sndio.sio_read = sio_read;
+ pContext->sndio.sio_start = sio_start;
+ pContext->sndio.sio_stop = sio_stop;
+ pContext->sndio.sio_initpar = sio_initpar;
+#endif
+
+ pContext->onUninit = mal_context_uninit__sndio;
+ pContext->onDeviceIDEqual = mal_context_is_device_id_equal__sndio;
+ pContext->onEnumDevices = mal_context_enumerate_devices__sndio;
+ pContext->onGetDeviceInfo = mal_context_get_device_info__sndio;
+ pContext->onDeviceInit = mal_device_init__sndio;
+ pContext->onDeviceUninit = mal_device_uninit__sndio;
+ pContext->onDeviceStart = mal_device__start_backend__sndio;
+ pContext->onDeviceStop = mal_device__stop_backend__sndio;
+ pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__sndio;
+ pContext->onDeviceMainLoop = mal_device__main_loop__sndio;
+
+ return MAL_SUCCESS;
+}
+#endif // sndio
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// audioio Backend
+//
+///////////////////////////////////////////////////////////////////////////////
+#ifdef MAL_HAS_AUDIOIO
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/audioio.h>
+
+void mal_construct_device_id__audioio(char* id, size_t idSize, const char* base, int deviceIndex)
+{
+ mal_assert(id != NULL);
+ mal_assert(idSize > 0);
+ mal_assert(deviceIndex >= 0);
+
+ size_t baseLen = strlen(base);
+ mal_assert(idSize > baseLen);
+
+ mal_strcpy_s(id, idSize, base);
+ mal_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
+}
+
+mal_result mal_extract_device_index_from_id__audioio(const char* id, const char* base, int* pIndexOut)
+{
+ mal_assert(id != NULL);
+ mal_assert(base != NULL);
+ mal_assert(pIndexOut != NULL);
+
+ size_t idLen = strlen(id);
+ size_t baseLen = strlen(base);
+ if (idLen <= baseLen) {
+ return MAL_ERROR; // Doesn't look like the id starts with the base.
+ }
+
+ if (strncmp(id, base, baseLen) != 0) {
+ return MAL_ERROR; // ID does not begin with base.
+ }
+
+ const char* deviceIndexStr = id + baseLen;
+ if (deviceIndexStr[0] == '\0') {
+ return MAL_ERROR; // No index specified in the ID.
+ }
+
+ if (pIndexOut) {
+ *pIndexOut = atoi(deviceIndexStr);
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_bool32 mal_context_is_device_id_equal__audioio(mal_context* pContext, const mal_device_id* pID0, const mal_device_id* pID1)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pID0 != NULL);
+ mal_assert(pID1 != NULL);
+ (void)pContext;
+
+ return mal_strcmp(pID0->audioio, pID1->audioio) == 0;
+}
+
+mal_format mal_format_from_encoding__audioio(unsigned int encoding, unsigned int precision)
+{
+ if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
+ return mal_format_u8;
+ } else {
+ if (mal_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
+ if (precision == 16) {
+ return mal_format_s16;
+ } else if (precision == 24) {
+ return mal_format_s24;
+ } else if (precision == 32) {
+ return mal_format_s32;
+ }
+ } else if (mal_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
+ if (precision == 16) {
+ return mal_format_s16;
+ } else if (precision == 24) {
+ return mal_format_s24;
+ } else if (precision == 32) {
+ return mal_format_s32;
+ }
+ }
+ }
+
+ return mal_format_unknown; // Encoding not supported.
+}
+
+mal_format mal_format_from_prinfo__audioio(struct audio_prinfo* prinfo)
+{
+ return mal_format_from_encoding__audioio(prinfo->encoding, prinfo->precision);
+}
+
+mal_result mal_context_get_device_info_from_fd__audioio(mal_context* pContext, mal_device_type deviceType, int fd, mal_device_info* pInfoOut)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(fd >= 0);
+ mal_assert(pInfoOut != NULL);
+
+ (void)pContext;
+ (void)deviceType;
+
+ audio_device_t fdDevice;
+ if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
+ return MAL_ERROR; // Failed to retrieve device info.
+ }
+
+ // Name.
+ mal_strcpy_s(pInfoOut->name, sizeof(pInfoOut->name), fdDevice.name);
+
+ // Supported formats. We get this by looking at the encodings.
+ int counter = 0;
+ for (;;) {
+ audio_encoding_t encoding;
+ mal_zero_object(&encoding);
+ encoding.index = counter;
+ if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
+ break;
+ }
+
+ mal_format format = mal_format_from_encoding__audioio(encoding.encoding, encoding.precision);
+ if (format != mal_format_unknown) {
+ pInfoOut->formats[pInfoOut->formatCount++] = format;
+ }
+
+ counter += 1;
+ }
+
+ audio_info_t fdInfo;
+ if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
+ return MAL_ERROR;
+ }
+
+ if (deviceType == mal_device_type_playback) {
+ pInfoOut->minChannels = fdInfo.play.channels;
+ pInfoOut->maxChannels = fdInfo.play.channels;
+ pInfoOut->minSampleRate = fdInfo.play.sample_rate;
+ pInfoOut->maxSampleRate = fdInfo.play.sample_rate;
+ } else {
+ pInfoOut->minChannels = fdInfo.record.channels;
+ pInfoOut->maxChannels = fdInfo.record.channels;
+ pInfoOut->minSampleRate = fdInfo.record.sample_rate;
+ pInfoOut->maxSampleRate = fdInfo.record.sample_rate;
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_enumerate_devices__audioio(mal_context* pContext, mal_enum_devices_callback_proc callback, void* pUserData)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(callback != NULL);
+
+ const int maxDevices = 64;
+
+ // Every device will be named "/dev/audioN", with a "/dev/audioctlN" equivalent. We use the "/dev/audioctlN"
+ // version here since we can open it even when another process has control of the "/dev/audioN" device.
+ char devpath[256];
+ for (int iDevice = 0; iDevice < maxDevices; ++iDevice) {
+ mal_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
+ mal_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
+
+ struct stat st;
+ if (stat(devpath, &st) < 0) {
+ break;
+ }
+
+ // The device exists, but we need to check if it's usable as playback and/or capture.
+ int fd;
+ mal_bool32 isTerminating = MAL_FALSE;
+
+ // Playback.
+ if (!isTerminating) {
+ fd = open(devpath, O_RDONLY, 0);
+ if (fd >= 0) {
+ // Supports playback.
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ mal_construct_device_id__audioio(deviceInfo.id.audioio, sizeof(deviceInfo.id.audioio), "/dev/audio", iDevice);
+ if (mal_context_get_device_info_from_fd__audioio(pContext, mal_device_type_playback, fd, &deviceInfo) == MAL_SUCCESS) {
+ isTerminating = !callback(pContext, mal_device_type_playback, &deviceInfo, pUserData);
+ }
+
+ close(fd);
+ }
+ }
+
+ // Capture.
+ if (!isTerminating) {
+ fd = open(devpath, O_WRONLY, 0);
+ if (fd >= 0) {
+ // Supports capture.
+ mal_device_info deviceInfo;
+ mal_zero_object(&deviceInfo);
+ mal_construct_device_id__audioio(deviceInfo.id.audioio, sizeof(deviceInfo.id.audioio), "/dev/audio", iDevice);
+ if (mal_context_get_device_info_from_fd__audioio(pContext, mal_device_type_capture, fd, &deviceInfo) == MAL_SUCCESS) {
+ isTerminating = !callback(pContext, mal_device_type_capture, &deviceInfo, pUserData);
+ }
+
+ close(fd);
+ }
+ }
+
+ if (isTerminating) {
+ break;
+ }
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_get_device_info__audioio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, mal_share_mode shareMode, mal_device_info* pDeviceInfo)
+{
+ mal_assert(pContext != NULL);
+ (void)shareMode;
+
+ // We need to open the "/dev/audioctlN" device to get the info. To do this we need to extract the number
+ // from the device ID which will be in "/dev/audioN" format.
+ int fd = -1;
+ int deviceIndex = -1;
+ char ctlid[256];
+ if (pDeviceID == NULL) {
+ // Default device.
+ mal_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
+ } else {
+ // Specific device. We need to convert from "/dev/audioN" to "/dev/audioctlN".
+ mal_result result = mal_extract_device_index_from_id__audioio(pDeviceID->audioio, "/dev/audio", &deviceIndex);
+ if (result != MAL_SUCCESS) {
+ return result;
+ }
+
+ mal_construct_device_id__audioio(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
+ }
+
+ fd = open(ctlid, (deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
+ if (fd == -1) {
+ return MAL_NO_DEVICE;
+ }
+
+ if (deviceIndex == -1) {
+ mal_strcpy_s(pDeviceInfo->id.audioio, sizeof(pDeviceInfo->id.audioio), "/dev/audio");
+ } else {
+ mal_construct_device_id__audioio(pDeviceInfo->id.audioio, sizeof(pDeviceInfo->id.audioio), "/dev/audio", deviceIndex);
+ }
+
+ mal_result result = mal_context_get_device_info_from_fd__audioio(pContext, deviceType, fd, pDeviceInfo);
+
+ close(fd);
+ return result;
+}
+
+void mal_device_uninit__audioio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ close(pDevice->audioio.fd);
+ mal_free(pDevice->audioio.pIntermediaryBuffer);
+}
+
+mal_result mal_device_init__audioio(mal_context* pContext, mal_device_type deviceType, const mal_device_id* pDeviceID, const mal_device_config* pConfig, mal_device* pDevice)
+{
+ (void)pContext;
+
+ mal_assert(pDevice != NULL);
+ mal_zero_object(&pDevice->audioio);
+
+ // The first thing to do is open the file.
+ const char* deviceName = "/dev/audio";
+ if (pDeviceID != NULL) {
+ deviceName = pDeviceID->audioio;
+ }
+
+ pDevice->audioio.fd = open(deviceName, (deviceType == mal_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
+ if (pDevice->audioio.fd == -1) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to open device.", MAL_FAILED_TO_OPEN_BACKEND_DEVICE);
+ }
+
+ audio_info_t fdInfo;
+ AUDIO_INITINFO(&fdInfo);
+
+ struct audio_prinfo* prinfo;
+ if (deviceType == mal_device_type_playback) {
+ prinfo = &fdInfo.play;
+ fdInfo.mode = AUMODE_PLAY;
+ } else {
+ prinfo = &fdInfo.record;
+ fdInfo.mode = AUMODE_RECORD;
+ }
+
+ // Format. Note that it looks like audioio does not support floating point formats. In this case
+ // we just fall back to s16.
+ switch (pDevice->format)
+ {
+ case mal_format_u8:
+ {
+ prinfo->encoding = AUDIO_ENCODING_ULINEAR;
+ prinfo->precision = 8;
+ } break;
+
+ case mal_format_s24:
+ {
+ prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
+ prinfo->precision = 24;
+ } break;
+
+ case mal_format_s32:
+ {
+ prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
+ prinfo->precision = 32;
+ } break;
+
+ case mal_format_s16:
+ case mal_format_f32:
+ default:
+ {
+ prinfo->encoding = (mal_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
+ prinfo->precision = 16;
+ } break;
+ }
+
+ // We always want to the use the devices native channel count and sample rate.
+ mal_device_info nativeInfo;
+ mal_result result = mal_context_get_device_info(pContext, deviceType, pDeviceID, pConfig->shareMode, &nativeInfo);
+ if (result != MAL_SUCCESS) {
+ close(pDevice->audioio.fd);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to retrieve device format.", result);
+ }
+
+ prinfo->channels = nativeInfo.maxChannels;
+ prinfo->sample_rate = nativeInfo.maxSampleRate;
+
+ // We need to apply the settings so far so we can get back the actual sample rate which we need for calculating
+ // the default buffer size below.
+ if (ioctl(pDevice->audioio.fd, AUDIO_SETINFO, &fdInfo) < 0) {
+ close(pDevice->audioio.fd);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to set device format. AUDIO_SETINFO failed.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+
+ if (ioctl(pDevice->audioio.fd, AUDIO_GETINFO, &fdInfo) < 0) {
+ close(pDevice->audioio.fd);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] AUDIO_GETINFO failed.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+
+ pDevice->internalFormat = mal_format_from_prinfo__audioio(prinfo);
+ if (pDevice->internalFormat == mal_format_unknown) {
+ close(pDevice->audioio.fd);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] The device's internal device format is not supported by mini_al. The device is unusable.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+
+ pDevice->internalChannels = prinfo->channels;
+ pDevice->internalSampleRate = prinfo->sample_rate;
+
+
+
+ // Try calculating an appropriate default buffer size.
+ if (pDevice->usingDefaultBufferSize) {
+ // CPU speed factor.
+ float fCPUSpeed = mal_calculate_cpu_speed_factor();
+
+ // Playback vs capture latency.
+ float fDeviceType = 1;
+
+ // Backend tax.
+ float fBackend = 1;
+
+ pDevice->bufferSizeInFrames = mal_calculate_default_buffer_size_in_frames(pConfig->performanceProfile, pDevice->internalSampleRate, fCPUSpeed*fDeviceType*fBackend);
+ }
+
+ // What mini_al calls a fragment, audioio calls a block.
+ mal_uint32 fragmentSizeInBytes = pDevice->bufferSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
+ if (fragmentSizeInBytes < 16) {
+ fragmentSizeInBytes = 16;
+ }
+
+
+ AUDIO_INITINFO(&fdInfo);
+ fdInfo.blocksize = fragmentSizeInBytes;
+ fdInfo.hiwat = mal_max(pDevice->periods, 5);
+ fdInfo.lowat = (unsigned int)(fdInfo.hiwat * 0.75);
+ if (ioctl(pDevice->audioio.fd, AUDIO_SETINFO, &fdInfo) < 0) {
+ close(pDevice->audioio.fd);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to set internal buffer size. AUDIO_SETINFO failed.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+
+ pDevice->periods = fdInfo.hiwat;
+ pDevice->bufferSizeInFrames = (fdInfo.blocksize * fdInfo.hiwat) / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
+
+ pDevice->audioio.fragmentSizeInFrames = fdInfo.blocksize / mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels);
+
+
+ // For the channel map, I'm not sure how to query the channel map (or if it's even possible). I'm just
+ // using mini_al's default channel map for now.
+ mal_get_standard_channel_map(mal_standard_channel_map_default, pDevice->internalChannels, pDevice->internalChannelMap);
+
+
+ // When not using MMAP mode we need to use an intermediary buffer to the data transfer between the client
+ // and device. Everything is done by the size of a fragment.
+ pDevice->audioio.pIntermediaryBuffer = mal_malloc(fdInfo.blocksize);
+ if (pDevice->audioio.pIntermediaryBuffer == NULL) {
+ close(pDevice->audioio.fd);
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to allocate memory for intermediary buffer.", MAL_OUT_OF_MEMORY);
+ }
+
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__start_backend__audioio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ // The device is started by the next calls to read() and write(). For playback it's simple - just read
+ // data from the client, then write it to the device with write() which will in turn start the device.
+ // For capture it's a bit less intuitive - we do nothing (it'll be started automatically by the first
+ // call to read().
+ if (pDevice->type == mal_device_type_playback) {
+ // Playback. Need to load the entire buffer, which means we need to write a fragment for each period.
+ for (mal_uint32 iPeriod = 0; iPeriod < pDevice->periods; iPeriod += 1) {
+ mal_device__read_frames_from_client(pDevice, pDevice->audioio.fragmentSizeInFrames, pDevice->audioio.pIntermediaryBuffer);
+
+ int bytesWritten = write(pDevice->audioio.fd, pDevice->audioio.pIntermediaryBuffer, pDevice->audioio.fragmentSizeInFrames * mal_get_bytes_per_frame(pDevice->internalFormat, pDevice->internalChannels));
+ if (bytesWritten == -1) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to send initial chunk of data to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE);
+ }
+ }
+ } else {
+ // Capture. Do nothing.
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__stop_backend__audioio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ if (ioctl(pDevice->audioio.fd, AUDIO_FLUSH, 0) < 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to stop device. AUDIO_FLUSH failed.", MAL_FAILED_TO_STOP_BACKEND_DEVICE);
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__break_main_loop__audioio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ pDevice->audioio.breakFromMainLoop = MAL_TRUE;
+ return MAL_SUCCESS;
+}
+
+mal_result mal_device__main_loop__audioio(mal_device* pDevice)
+{
+ mal_assert(pDevice != NULL);
+
+ pDevice->audioio.breakFromMainLoop = MAL_FALSE;
+ while (!pDevice->audioio.breakFromMainLoop) {
+ // Break from the main loop if the device isn't started anymore. Likely what's happened is the application
+ // has requested that the device be stopped.
+ if (!mal_device_is_started(pDevice)) {
+ break;
+ }
+
+ if (pDevice->type == mal_device_type_playback) {
+ // Playback.
+ mal_device__read_frames_from_client(pDevice, pDevice->audioio.fragmentSizeInFrames, pDevice->audioio.pIntermediaryBuffer);
+
+ int bytesWritten = write(pDevice->audioio.fd, pDevice->audioio.pIntermediaryBuffer, pDevice->audioio.fragmentSizeInFrames * pDevice->internalChannels * mal_get_bytes_per_sample(pDevice->internalFormat));
+ if (bytesWritten < 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to send data from the client to the device.", MAL_FAILED_TO_SEND_DATA_TO_DEVICE);
+ }
+ } else {
+ // Capture.
+ int bytesRead = read(pDevice->audioio.fd, pDevice->audioio.pIntermediaryBuffer, pDevice->audioio.fragmentSizeInFrames * mal_get_bytes_per_sample(pDevice->internalFormat));
+ if (bytesRead < 0) {
+ return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[audioio] Failed to read data from the device to be sent to the client.", MAL_FAILED_TO_READ_DATA_FROM_DEVICE);
+ }
+
+ mal_uint32 framesRead = (mal_uint32)bytesRead / pDevice->internalChannels / mal_get_bytes_per_sample(pDevice->internalFormat);
+ mal_device__send_frames_to_client(pDevice, framesRead, pDevice->audioio.pIntermediaryBuffer);
+ }
+ }
+
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_uninit__audioio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+ mal_assert(pContext->backend == mal_backend_audioio);
+
+ (void)pContext;
+ return MAL_SUCCESS;
+}
+
+mal_result mal_context_init__audioio(mal_context* pContext)
+{
+ mal_assert(pContext != NULL);
+
+ pContext->onUninit = mal_context_uninit__audioio;
+ pContext->onDeviceIDEqual = mal_context_is_device_id_equal__audioio;
+ pContext->onEnumDevices = mal_context_enumerate_devices__audioio;
+ pContext->onGetDeviceInfo = mal_context_get_device_info__audioio;
+ pContext->onDeviceInit = mal_device_init__audioio;
+ pContext->onDeviceUninit = mal_device_uninit__audioio;
+ pContext->onDeviceStart = mal_device__start_backend__audioio;
+ pContext->onDeviceStop = mal_device__stop_backend__audioio;
+ pContext->onDeviceBreakMainLoop = mal_device__break_main_loop__audioio;
+ pContext->onDeviceMainLoop = mal_device__main_loop__audioio;
+
+ return MAL_SUCCESS;
+}
+#endif // audioio
+
+
+///////////////////////////////////////////////////////////////////////////////
+//
// OSS Backend
//
///////////////////////////////////////////////////////////////////////////////
@@ -14747,10 +16463,10 @@ mal_result mal_context_get_device_info__oss(mal_context* pContext, mal_device_ty
if ((formatMask & AFMT_U8) != 0) {
pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_u8;
}
- if ((formatMask & AFMT_S16_LE) != 0) {
+ if (((formatMask & AFMT_S16_LE) != 0 && mal_is_little_endian()) || (AFMT_S16_BE && mal_is_big_endian())) {
pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s16;
}
- if ((formatMask & AFMT_S32_LE) != 0) {
+ if (((formatMask & AFMT_S32_LE) != 0 && mal_is_little_endian()) || (AFMT_S32_BE && mal_is_big_endian())) {
pDeviceInfo->formats[pDeviceInfo->formatCount++] = mal_format_s32;
}
@@ -14804,10 +16520,10 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con
// Format.
int ossFormat = AFMT_U8;
switch (pDevice->format) {
- case mal_format_s16: ossFormat = AFMT_S16_LE; break;
- case mal_format_s24: ossFormat = AFMT_S32_LE; break;
- case mal_format_s32: ossFormat = AFMT_S32_LE; break;
- case mal_format_f32: ossFormat = AFMT_S32_LE; break;
+ case mal_format_s16: ossFormat = (mal_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
+ case mal_format_s24: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
+ case mal_format_s32: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
+ case mal_format_f32: ossFormat = (mal_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
case mal_format_u8:
default: ossFormat = AFMT_U8; break;
}
@@ -14817,14 +16533,24 @@ mal_result mal_device_init__oss(mal_context* pContext, mal_device_type type, con
return mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] Failed to set format.", MAL_FORMAT_NOT_SUPPORTED);
}
- switch (ossFormat) {
- case AFMT_U8: pDevice->internalFormat = mal_format_u8; break;
- case AFMT_S16_LE: pDevice->internalFormat = mal_format_s16; break;
- case AFMT_S32_LE: pDevice->internalFormat = mal_format_s32; break;
- default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED);
+ if (ossFormat == AFMT_U8) {
+ pDevice->internalFormat = mal_format_u8;
+ } else {
+ if (mal_is_little_endian()) {
+ switch (ossFormat) {
+ case AFMT_S16_LE: pDevice->internalFormat = mal_format_s16; break;
+ case AFMT_S32_LE: pDevice->internalFormat = mal_format_s32; break;
+ default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+ } else {
+ switch (ossFormat) {
+ case AFMT_S16_BE: pDevice->internalFormat = mal_format_s16; break;
+ case AFMT_S32_BE: pDevice->internalFormat = mal_format_s32; break;
+ default: mal_post_error(pDevice, MAL_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by mini_al.", MAL_FORMAT_NOT_SUPPORTED);
+ }
+ }
}
-
// Channels.
int ossChannels = (int)pConfig->channels;
ossResult = ioctl(pDevice->oss.fd, SNDCTL_DSP_CHANNELS, &ossChannels);
@@ -15538,7 +17264,7 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type,
pFormat->bitsPerSample = mal_get_bytes_per_sample(pDevice->format)*8;
pFormat->containerSize = pFormat->bitsPerSample; // Always tightly packed for now.
pFormat->channelMask = mal_channel_map_to_channel_mask__opensl(pConfig->channelMap, pFormat->numChannels);
- pFormat->endianness = SL_BYTEORDER_LITTLEENDIAN;
+ pFormat->endianness = (mal_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
// Android has a few restrictions on the format as documented here: https://developer.android.com/ndk/guides/audio/opensl-for-android.html
// - Only mono and stereo is supported.
@@ -15591,7 +17317,8 @@ mal_result mal_device_init__opensl(mal_context* pContext, mal_device_type type,
// Set the output device.
if (pDeviceID != NULL) {
- MAL_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &pDeviceID->opensl);
+ SLuint32 deviceID_OpenSL = pDeviceID->opensl;
+ MAL_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
}
SLDataSource source;
@@ -17449,6 +19176,62 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
mal_CoInitializeEx(pDevice->pContext, NULL, 0); // 0 = COINIT_MULTITHREADED
#endif
+#if 1
+ // When the device is being initialized it's initial state is set to MAL_STATE_UNINITIALIZED. Before returning from
+ // mal_device_init(), the state needs to be set to something valid. In mini_al the device's default state immediately
+ // after initialization is stopped, so therefore we need to mark the device as such. mini_al will wait on the worker
+ // thread to signal an event to know when the worker thread is ready for action.
+ mal_device__set_state(pDevice, MAL_STATE_STOPPED);
+ mal_event_signal(&pDevice->stopEvent);
+
+ for (;;) {
+ // We wait on an event to know when something has requested that the device be started and the main loop entered.
+ mal_event_wait(&pDevice->wakeupEvent);
+
+ // Default result code.
+ pDevice->workResult = MAL_SUCCESS;
+
+ // If the reason for the wake up is that we are terminating, just break from the loop.
+ if (mal_device__get_state(pDevice) == MAL_STATE_UNINITIALIZED) {
+ break;
+ }
+
+ // Getting to this point means the device is wanting to get started. The function that has requested that the device
+ // be started will be waiting on an event (pDevice->startEvent) which means we need to make sure we signal the event
+ // in both the success and error case. It's important that the state of the device is set _before_ signaling the event.
+ mal_assert(mal_device__get_state(pDevice) == MAL_STATE_STARTING);
+
+ pDevice->workResult = pDevice->pContext->onDeviceStart(pDevice);
+ if (pDevice->workResult != MAL_SUCCESS) {
+ mal_device__set_state(pDevice, MAL_STATE_STOPPED);
+ mal_event_signal(&pDevice->startEvent);
+ continue;
+ }
+
+ // At this point the device should be started.
+ mal_device__set_state(pDevice, MAL_STATE_STARTED);
+ mal_event_signal(&pDevice->startEvent);
+
+
+ // Now we just enter the main loop. When the main loop is terminated the device needs to be marked as stopped. This can
+ // be broken with mal_device__break_main_loop().
+ pDevice->pContext->onDeviceMainLoop(pDevice);
+
+
+ // Getting here means we have broken from the main loop which happens the application has requested that device be stopped.
+ pDevice->pContext->onDeviceStop(pDevice);
+
+ // After the device has stopped, make sure an event is posted.
+ mal_stop_proc onStop = pDevice->onStop;
+ if (onStop) {
+ onStop(pDevice);
+ }
+
+ // A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue.
+ mal_device__set_state(pDevice, MAL_STATE_STOPPED);
+ mal_event_signal(&pDevice->stopEvent);
+ }
+#else
// This is only used to prevent posting onStop() when the device is first initialized.
mal_bool32 skipNextStopEvent = MAL_TRUE;
@@ -17500,6 +19283,7 @@ mal_thread_result MAL_THREADCALL mal_worker_thread(void* pData)
// Now we just enter the main loop. The main loop can be broken with mal_device__break_main_loop().
pDevice->pContext->onDeviceMainLoop(pDevice);
}
+#endif
// Make sure we aren't continuously waiting on a stop event.
mal_event_signal(&pDevice->stopEvent); // <-- Is this still needed?
@@ -17668,20 +19452,6 @@ mal_result mal_context_uninit_backend_apis(mal_context* pContext)
return result;
}
-const mal_backend g_malDefaultBackends[] = {
- mal_backend_wasapi,
- mal_backend_dsound,
- mal_backend_winmm,
- mal_backend_coreaudio,
- mal_backend_oss,
- mal_backend_pulseaudio,
- mal_backend_alsa,
- mal_backend_jack,
- mal_backend_opensl,
- mal_backend_openal,
- mal_backend_sdl,
- mal_backend_null
-};
mal_bool32 mal_context_is_backend_asynchronous(mal_context* pContext)
{
@@ -17765,6 +19535,18 @@ mal_result mal_context_init(const mal_backend backends[], mal_uint32 backendCoun
result = mal_context_init__coreaudio(pContext);
} break;
#endif
+ #ifdef MAL_HAS_SNDIO
+ case mal_backend_sndio:
+ {
+ result = mal_context_init__sndio(pContext);
+ } break;
+ #endif
+ #ifdef MAL_HAS_AUDIOIO
+ case mal_backend_audioio:
+ {
+ result = mal_context_init__audioio(pContext);
+ } break;
+ #endif
#ifdef MAL_HAS_OSS
case mal_backend_oss:
{
@@ -18387,19 +20169,6 @@ mal_uint32 mal_device_get_buffer_size_in_bytes(mal_device* pDevice)
return pDevice->bufferSizeInFrames * pDevice->channels * mal_get_bytes_per_sample(pDevice->format);
}
-mal_uint32 mal_get_bytes_per_sample(mal_format format)
-{
- mal_uint32 sizes[] = {
- 0, // unknown
- 1, // u8
- 2, // s16
- 3, // s24
- 4, // s32
- 4, // f32
- };
- return sizes[format];
-}
-
mal_context_config mal_context_config_init(mal_log_proc onLog)
{
mal_context_config config;
@@ -18462,6 +20231,7 @@ mal_device_config mal_device_config_init_ex(mal_format format, mal_uint32 channe
return config;
}
+#endif // MAL_NO_DEVICE_IO
void mal_get_standard_channel_map_microsoft(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS])
@@ -18863,6 +20633,65 @@ void mal_get_standard_channel_map_vorbis(mal_uint32 channels, mal_channel channe
}
}
+void mal_get_standard_channel_map_sndio(mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS])
+{
+ switch (channels)
+ {
+ case 1:
+ {
+ channelMap[0] = MAL_CHANNEL_MONO;
+ } break;
+
+ case 2:
+ {
+ channelMap[0] = MAL_CHANNEL_LEFT;
+ channelMap[1] = MAL_CHANNEL_RIGHT;
+ } break;
+
+ case 3:
+ {
+ channelMap[0] = MAL_CHANNEL_FRONT_LEFT;
+ channelMap[1] = MAL_CHANNEL_FRONT_RIGHT;
+ channelMap[2] = MAL_CHANNEL_FRONT_CENTER;
+ } break;
+
+ case 4:
+ {
+ channelMap[0] = MAL_CHANNEL_FRONT_LEFT;
+ channelMap[1] = MAL_CHANNEL_FRONT_RIGHT;
+ channelMap[2] = MAL_CHANNEL_BACK_LEFT;
+ channelMap[3] = MAL_CHANNEL_BACK_RIGHT;
+ } break;
+
+ case 5:
+ {
+ channelMap[0] = MAL_CHANNEL_FRONT_LEFT;
+ channelMap[1] = MAL_CHANNEL_FRONT_RIGHT;
+ channelMap[2] = MAL_CHANNEL_BACK_LEFT;
+ channelMap[3] = MAL_CHANNEL_BACK_RIGHT;
+ channelMap[4] = MAL_CHANNEL_FRONT_CENTER;
+ } break;
+
+ case 6:
+ default:
+ {
+ channelMap[0] = MAL_CHANNEL_FRONT_LEFT;
+ channelMap[1] = MAL_CHANNEL_FRONT_RIGHT;
+ channelMap[2] = MAL_CHANNEL_BACK_LEFT;
+ channelMap[3] = MAL_CHANNEL_BACK_RIGHT;
+ channelMap[4] = MAL_CHANNEL_FRONT_CENTER;
+ channelMap[5] = MAL_CHANNEL_LFE;
+ } break;
+ }
+
+ // Remainder.
+ if (channels > 6) {
+ for (mal_uint32 iChannel = 6; iChannel < MAL_MAX_CHANNELS; ++iChannel) {
+ channelMap[iChannel] = (mal_channel)(MAL_CHANNEL_AUX_0 + (iChannel-6));
+ }
+ }
+}
+
void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS])
{
switch (standardChannelMap)
@@ -18886,6 +20715,11 @@ void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, m
{
mal_get_standard_channel_map_vorbis(channels, channelMap);
} break;
+
+ case mal_standard_channel_map_sndio:
+ {
+ mal_get_standard_channel_map_sndio(channels, channelMap);
+ } break;
case mal_standard_channel_map_microsoft:
default:
@@ -22555,6 +24389,23 @@ mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOu
return MAL_SUCCESS;
}
+mal_result mal_src_set_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut)
+{
+ if (pSRC == NULL) {
+ return MAL_INVALID_ARGS;
+ }
+
+ // Must have a sample rate of > 0.
+ if (sampleRateIn == 0 || sampleRateOut == 0) {
+ return MAL_INVALID_ARGS;
+ }
+
+ mal_atomic_exchange_32(&pSRC->config.sampleRateIn, sampleRateIn);
+ mal_atomic_exchange_32(&pSRC->config.sampleRateOut, sampleRateOut);
+
+ return MAL_SUCCESS;
+}
+
mal_uint64 mal_src_read_deinterleaved(mal_src* pSRC, mal_uint64 frameCount, void** ppSamplesOut, void* pUserData)
{
if (pSRC == NULL || frameCount == 0 || ppSamplesOut == NULL) {
@@ -23056,8 +24907,8 @@ mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount
maxInputSamplesAvailableInCache = pSRC->sinc.inputFrameCount;
}
- // If the last of the input data has been loaded, we need to ensure we don't read into it if it's configured such.
- if (pSRC->config.neverConsumeEndOfInput && pSRC->isEndOfInputLoaded) {
+ // Never consume the tail end of the input data if requested.
+ if (pSRC->config.neverConsumeEndOfInput) {
if (maxInputSamplesAvailableInCache >= pSRC->config.sinc.windowWidth) {
maxInputSamplesAvailableInCache -= pSRC->config.sinc.windowWidth;
} else {
@@ -23249,6 +25100,15 @@ mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount
}
// Read more data from the client if required.
+ if (pSRC->isEndOfInputLoaded) {
+ pSRC->isEndOfInputLoaded = MAL_FALSE;
+ break;
+ }
+
+ // Everything beyond this point is reloading. If we're at the end of the input data we do _not_ want to try reading any more in this function call. If the
+ // caller wants to keep trying, they can reload their internal data sources and call this function again. We should never be
+ mal_assert(pSRC->isEndOfInputLoaded == MAL_FALSE);
+
if (pSRC->sinc.inputFrameCount <= pSRC->config.sinc.windowWidth || availableOutputFrames == 0) {
float* ppInputDst[MAL_MAX_CHANNELS] = {0};
for (mal_uint32 iChannel = 0; iChannel < pSRC->config.channels; iChannel += 1) {
@@ -23257,20 +25117,29 @@ mal_uint64 mal_src_read_deinterleaved__sinc(mal_src* pSRC, mal_uint64 frameCount
// Now read data from the client.
mal_uint32 framesToReadFromClient = mal_countof(pSRC->sinc.input[0]) - (pSRC->config.sinc.windowWidth + pSRC->sinc.inputFrameCount);
+
+ mal_uint32 framesReadFromClient = 0;
if (framesToReadFromClient > 0) {
- mal_uint32 framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData);
- if (framesReadFromClient != framesToReadFromClient) {
- pSRC->isEndOfInputLoaded = MAL_TRUE;
- } else {
- pSRC->isEndOfInputLoaded = MAL_FALSE;
- }
+ framesReadFromClient = pSRC->config.onReadDeinterleaved(pSRC, framesToReadFromClient, (void**)ppInputDst, pUserData);
+ }
+
+ if (framesReadFromClient != framesToReadFromClient) {
+ pSRC->isEndOfInputLoaded = MAL_TRUE;
+ } else {
+ pSRC->isEndOfInputLoaded = MAL_FALSE;
+ }
- if (framesReadFromClient != 0) {
- pSRC->sinc.inputFrameCount += framesReadFromClient;
+ if (framesReadFromClient != 0) {
+ pSRC->sinc.inputFrameCount += framesReadFromClient;
+ } else {
+ // We couldn't get anything more from the client. If no more output samples can be computed from the available input samples
+ // we need to return.
+ if (pSRC->config.neverConsumeEndOfInput) {
+ if ((pSRC->sinc.inputFrameCount * inverseFactor) <= pSRC->config.sinc.windowWidth) {
+ break;
+ }
} else {
- // We couldn't get anything more from the client. If no more output samples can be computed from the available input samples
- // we need to return.
- if (((pSRC->sinc.timeIn - pSRC->sinc.inputFrameCount) * inverseFactor) < 1) {
+ if ((pSRC->sinc.inputFrameCount * inverseFactor) < 1) {
break;
}
}
@@ -23712,6 +25581,28 @@ mal_result mal_dsp_set_output_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateOu
return mal_dsp_refresh_sample_rate(pDSP);
}
+mal_result mal_dsp_set_sample_rate(mal_dsp* pDSP, mal_uint32 sampleRateIn, mal_uint32 sampleRateOut)
+{
+ if (pDSP == NULL) {
+ return MAL_INVALID_ARGS;
+ }
+
+ // Must have a sample rate of > 0.
+ if (sampleRateIn == 0 || sampleRateOut == 0) {
+ return MAL_INVALID_ARGS;
+ }
+
+ // Must have been initialized with allowDynamicSampleRate.
+ if (!pDSP->isDynamicSampleRateAllowed) {
+ return MAL_INVALID_OPERATION;
+ }
+
+ mal_atomic_exchange_32(&pDSP->src.config.sampleRateIn, sampleRateIn);
+ mal_atomic_exchange_32(&pDSP->src.config.sampleRateOut, sampleRateOut);
+
+ return mal_dsp_refresh_sample_rate(pDSP);
+}
+
mal_uint64 mal_dsp_read(mal_dsp* pDSP, mal_uint64 frameCount, void* pFramesOut, void* pUserData)
{
if (pDSP == NULL || pFramesOut == NULL) return 0;
@@ -23970,26 +25861,6 @@ void mal_aligned_free(void* p)
mal_free(((void**)p)[-1]);
}
-const char* mal_get_backend_name(mal_backend backend)
-{
- switch (backend)
- {
- case mal_backend_null: return "Null";
- case mal_backend_wasapi: return "WASAPI";
- case mal_backend_dsound: return "DirectSound";
- case mal_backend_winmm: return "WinMM";
- case mal_backend_alsa: return "ALSA";
- case mal_backend_pulseaudio: return "PulseAudio";
- case mal_backend_jack: return "JACK";
- case mal_backend_coreaudio: return "Core Audio";
- case mal_backend_oss: return "OSS";
- case mal_backend_opensl: return "OpenSL|ES";
- case mal_backend_openal: return "OpenAL";
- case mal_backend_sdl: return "SDL";
- default: return "Unknown";
- }
-}
-
const char* mal_get_format_name(mal_format format)
{
switch (format)
@@ -24012,137 +25883,20 @@ void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint
}
-typedef struct
-{
- mal_uint8* pInputFrames;
- mal_uint32 framesRemaining;
-} mal_calculate_cpu_speed_factor_data;
-
-mal_uint32 mal_calculate_cpu_speed_factor__on_read(mal_dsp* pDSP, mal_uint32 framesToRead, void* pFramesOut, void* pUserData)
-{
- mal_calculate_cpu_speed_factor_data* pData = (mal_calculate_cpu_speed_factor_data*)pUserData;
- mal_assert(pData != NULL);
-
- if (framesToRead > pData->framesRemaining) {
- framesToRead = pData->framesRemaining;
- }
-
- mal_copy_memory(pFramesOut, pData->pInputFrames, framesToRead*pDSP->formatConverterIn.config.channels * sizeof(*pData->pInputFrames));
-
- pData->pInputFrames += framesToRead;
- pData->framesRemaining -= framesToRead;
-
- return framesToRead;
-}
-
-float mal_calculate_cpu_speed_factor()
-{
- // Our profiling test is based on how quick it can process 1 second worth of samples through mini_al's data conversion pipeline.
-
- // This factor is multiplied with the profiling time. May need to fiddle with this to get an accurate value.
- double f = 1000;
-
- // Experiment: Reduce the factor a little when debug mode is used to reduce a blowout.
-#if !defined(NDEBUG) || defined(_DEBUG)
- f /= 2;
-#endif
-
- mal_uint32 sampleRateIn = 44100;
- mal_uint32 sampleRateOut = 48000;
- mal_uint32 channelsIn = 2;
- mal_uint32 channelsOut = 6;
-
- // Using the heap here to avoid an unnecessary static memory allocation. Also too big for the stack.
- mal_uint8* pInputFrames = NULL;
- float* pOutputFrames = NULL;
-
- size_t inputDataSize = sampleRateIn * channelsIn * sizeof(*pInputFrames);
- size_t outputDataSize = sampleRateOut * channelsOut * sizeof(*pOutputFrames);
-
- void* pData = mal_malloc(inputDataSize + outputDataSize);
- if (pData == NULL) {
- return 1;
- }
-
- pInputFrames = (mal_uint8*)pData;
- pOutputFrames = (float*)(pInputFrames + inputDataSize);
-
-
-
-
- mal_calculate_cpu_speed_factor_data data;
- data.pInputFrames = pInputFrames;
- data.framesRemaining = sampleRateIn;
-
- mal_dsp_config config = mal_dsp_config_init(mal_format_u8, channelsIn, sampleRateIn, mal_format_f32, channelsOut, sampleRateOut, mal_calculate_cpu_speed_factor__on_read, &data);
-
- // Use linear sample rate conversion because it's the simplest and least likely to cause skewing as a result of tweaks to default
- // configurations in the future.
- config.srcAlgorithm = mal_src_algorithm_linear;
-
- // Experiment: Disable SIMD extensions when profiling just to try and keep things a bit more consistent. The idea is to get a general
- // indication on the speed of the system, but SIMD is used more heavily in the DSP pipeline than in the general case which may make
- // the results a little less realistic.
- config.noSSE2 = MAL_TRUE;
- config.noAVX2 = MAL_TRUE;
- config.noAVX512 = MAL_TRUE;
- config.noNEON = MAL_TRUE;
-
- mal_dsp dsp;
- mal_result result = mal_dsp_init(&config, &dsp);
- if (result != MAL_SUCCESS) {
- mal_free(pData);
- return 1;
- }
-
-
- int iterationCount = 2;
-
- mal_timer timer;
- mal_timer_init(&timer);
- double startTime = mal_timer_get_time_in_seconds(&timer);
- {
- for (int i = 0; i < iterationCount; ++i) {
- mal_dsp_read(&dsp, sampleRateOut, pOutputFrames, &data);
- data.pInputFrames = pInputFrames;
- data.framesRemaining = sampleRateIn;
- }
- }
- double executionTimeInSeconds = mal_timer_get_time_in_seconds(&timer) - startTime;
- executionTimeInSeconds /= iterationCount;
-
-
- mal_free(pData);
-
- // Guard against extreme blowouts.
- return (float)mal_clamp(executionTimeInSeconds * f, 0.1, 100.0);
-}
-
-mal_uint32 mal_scale_buffer_size(mal_uint32 baseBufferSize, float scale)
-{
- return mal_max(1, (mal_uint32)(baseBufferSize*scale));
-}
-
-mal_uint32 mal_calculate_default_buffer_size_in_frames(mal_performance_profile performanceProfile, mal_uint32 sampleRate, float scale)
+mal_uint32 mal_get_bytes_per_sample(mal_format format)
{
- mal_uint32 baseLatency;
- if (performanceProfile == mal_performance_profile_low_latency) {
- baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_LOW_LATENCY;
- } else {
- baseLatency = MAL_BASE_BUFFER_SIZE_IN_MILLISECONDS_CONSERVATIVE;
- }
-
- mal_uint32 sampleRateMS = (sampleRate/1000);
-
- mal_uint32 minBufferSize = sampleRateMS * mal_min(baseLatency / 5, 1); // <-- Guard against multiply by zero.
- mal_uint32 maxBufferSize = sampleRateMS * (baseLatency * 40);
-
- mal_uint32 bufferSize = mal_scale_buffer_size((sampleRate/1000) * baseLatency, scale);
- return mal_clamp(bufferSize, minBufferSize, maxBufferSize);
+ mal_uint32 sizes[] = {
+ 0, // unknown
+ 1, // u8
+ 2, // s16
+ 3, // s24
+ 4, // s32
+ 4, // f32
+ };
+ return sizes[format];
}
-
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
@@ -24211,6 +25965,11 @@ mal_result mal_decoder__init_dsp(mal_decoder* pDecoder, const mal_decoder_config
pDecoder->internalFormat, pDecoder->internalChannels, pDecoder->internalSampleRate, pDecoder->internalChannelMap,
pDecoder->outputFormat, pDecoder->outputChannels, pDecoder->outputSampleRate, pDecoder->outputChannelMap,
onRead, pDecoder);
+ dspConfig.channelMixMode = pConfig->channelMixMode;
+ dspConfig.ditherMode = pConfig->ditherMode;
+ dspConfig.srcAlgorithm = pConfig->srcAlgorithm;
+ dspConfig.sinc = pConfig->src.sinc;
+
return mal_dsp_init(&dspConfig, &pDecoder->dsp);
}
@@ -25624,6 +27383,21 @@ mal_uint64 mal_sine_wave_read(mal_sine_wave* pSineWave, mal_uint64 count, float*
// REVISION HISTORY
// ================
//
+// v0.8.5-rc - 2018-xx-xx
+// - Fix a bug where an incorrect number of samples is returned from sinc resampling.
+//
+// v0.8.4 - 2018-08-06
+// - Add sndio backend for OpenBSD.
+// - Add audioio backend for NetBSD.
+// - Drop support for the OSS backend on everything except FreeBSD and DragonFly BSD.
+// - Formats are now native-endian (were previously little-endian).
+// - Mark some APIs as deprecated:
+// - mal_src_set_input_sample_rate() and mal_src_set_output_sample_rate() are replaced with mal_src_set_sample_rate().
+// - mal_dsp_set_input_sample_rate() and mal_dsp_set_output_sample_rate() are replaced with mal_dsp_set_sample_rate().
+// - Fix a bug when capturing using the WASAPI backend.
+// - Fix some aliasing issues with resampling, specifically when increasing the sample rate.
+// - Fix warnings.
+//
// v0.8.3 - 2018-07-15
// - Fix a crackling bug when resampling in capture mode.
// - Core Audio: Fix a bug where capture does not work.
diff --git a/src/models.c b/src/models.c
index b389333b..b1abe66d 100644
--- a/src/models.c
+++ b/src/models.c
@@ -1771,7 +1771,7 @@ void DrawModelWiresEx(Model model, Vector3 position, Vector3 rotationAxis, float
// Draw a billboard
void DrawBillboard(Camera camera, Texture2D texture, Vector3 center, float size, Color tint)
{
- Rectangle sourceRec = { 0, 0, texture.width, texture.height };
+ Rectangle sourceRec = { 0.0f, 0.0f, (float)texture.width, (float)texture.height };
DrawBillboardRec(camera, texture, sourceRec, center, size, tint);
}
@@ -1837,9 +1837,9 @@ void DrawBoundingBox(BoundingBox box, Color color)
{
Vector3 size;
- size.x = fabs(box.max.x - box.min.x);
- size.y = fabs(box.max.y - box.min.y);
- size.z = fabs(box.max.z - box.min.z);
+ size.x = (float)fabs(box.max.x - box.min.x);
+ size.y = (float)fabs(box.max.y - box.min.y);
+ size.z = (float)fabs(box.max.z - box.min.z);
Vector3 center = { box.min.x + size.x/2.0f, box.min.y + size.y/2.0f, box.min.z + size.z/2.0f };
@@ -2206,9 +2206,9 @@ void MeshBinormals(Mesh *mesh)
Vector3 tangent = { mesh->tangents[i*4 + 0], mesh->tangents[i*4 + 1], mesh->tangents[i*4 + 2] };
float tangentW = mesh->tangents[i*4 + 3];
- Vector3 binormal = Vector3Multiply(Vector3CrossProduct(normal, tangent), tangentW);
// TODO: Register computed binormal in mesh->binormal ?
+ // Vector3 binormal = Vector3Multiply( Vector3CrossProduct( normal, tangent ), tangentW );
}
}
diff --git a/src/physac.h b/src/physac.h
index 6b78fcc6..38789a6d 100644
--- a/src/physac.h
+++ b/src/physac.h
@@ -196,6 +196,7 @@ extern "C" { // Prevents name mangling of fun
// Module Functions Declaration
//----------------------------------------------------------------------------------
PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread
+PHYSACDEF void RunPhysicsStep(void); // Run physics step, to be used if PHYSICS_NO_THREADS is set in your main loop
PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled
PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force
PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters
@@ -245,19 +246,18 @@ PHYSACDEF void ClosePhysics(void);
#endif
// Time management functionality
+#include <time.h> // Required for: time(), clock_gettime()
#if defined(_WIN32)
// Functions required to query time on Windows
int __stdcall QueryPerformanceCounter(unsigned long long int *lpPerformanceCount);
int __stdcall QueryPerformanceFrequency(unsigned long long int *lpFrequency);
- #include <time.h>
#elif defined(__linux__)
#if _POSIX_C_SOURCE < 199309L
#undef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 199309L // Required for CLOCK_MONOTONIC if compiled with c99 without gnu ext.
#endif
#include <sys/time.h> // Required for: timespec
- #include <time.h> // Required for: clock_gettime()
-#elif defined(__APPLE__) // macOS also defines __MACH__
+#elif defined(__APPLE__) // macOS also defines __MACH__
#include <mach/mach_time.h> // Required for: mach_absolute_time()
#endif
@@ -356,7 +356,10 @@ PHYSACDEF void InitPhysics(void)
// Create physics thread using POSIXS thread libraries
pthread_create(&physicsThreadId, NULL, &PhysicsLoop, NULL);
#endif
-
+
+ // Initialize high resolution timer
+ InitTimer();
+
#if defined(PHYSAC_DEBUG)
printf("[PHYSAC] physics module initialized successfully\n");
#endif
@@ -1010,33 +1013,10 @@ static void *PhysicsLoop(void *arg)
physicsThreadEnabled = true;
accumulator = 0;
- // Initialize high resolution timer
- InitTimer();
-
// Physics update loop
while (physicsThreadEnabled)
{
- // Calculate current time
- currentTime = GetCurrentTime();
-
- // Calculate current delta time
- deltaTime = currentTime - startTime;
-
- // Store the time elapsed since the last frame began
- accumulator += deltaTime;
-
- // Clamp accumulator to max time step to avoid bad performance
- MathClamp(&accumulator, 0.0, PHYSAC_MAX_TIMESTEP);
-
- // Fixed time stepping loop
- while (accumulator >= PHYSAC_DESIRED_DELTATIME)
- {
- PhysicsStep();
- accumulator -= deltaTime;
- }
-
- // Record the starting of this frame
- startTime = currentTime;
+ RunPhysicsStep();
}
// Unitialize physics manifolds dynamic memory allocations
@@ -1160,6 +1140,32 @@ static void PhysicsStep(void)
}
}
+// Wrapper to ensure PhysicsStep is run with at a fixed time step
+PHYSACDEF void RunPhysicsStep(void)
+{
+ // Calculate current time
+ currentTime = GetCurrentTime();
+
+ // Calculate current delta time
+ deltaTime = currentTime - startTime;
+
+ // Store the time elapsed since the last frame began
+ accumulator += deltaTime;
+
+ // Clamp accumulator to max time step to avoid bad performance
+ MathClamp(&accumulator, 0.0, PHYSAC_MAX_TIMESTEP);
+
+ // Fixed time stepping loop
+ while (accumulator >= PHYSAC_DESIRED_DELTATIME)
+ {
+ PhysicsStep();
+ accumulator -= deltaTime;
+ }
+
+ // Record the starting of this frame
+ startTime = currentTime;
+}
+
// Finds a valid index for a new manifold initialization
static int FindAvailableManifoldIndex()
{
@@ -2048,4 +2054,4 @@ static inline Vector2 Mat2MultiplyVector2(Mat2 matrix, Vector2 vector)
return (Vector2){ matrix.m00*vector.x + matrix.m01*vector.y, matrix.m10*vector.x + matrix.m11*vector.y };
}
-#endif // PHYSAC_IMPLEMENTATION \ No newline at end of file
+#endif // PHYSAC_IMPLEMENTATION
diff --git a/src/raymath.h b/src/raymath.h
index 4e100bc3..c2dc1cf4 100644
--- a/src/raymath.h
+++ b/src/raymath.h
@@ -296,12 +296,12 @@ RMDEF Vector3 Vector3Perpendicular(Vector3 v)
{
Vector3 result = { 0 };
- float min = fabs(v.x);
+ float min = (float) fabs(v.x);
Vector3 cardinalAxis = {1.0f, 0.0f, 0.0f};
if (fabs(v.y) < min)
{
- min = fabs(v.y);
+ min = (float) fabs(v.y);
Vector3 tmp = {0.0f, 1.0f, 0.0f};
cardinalAxis = tmp;
}
@@ -840,28 +840,28 @@ RMDEF Matrix MatrixFrustum(double left, double right, double bottom, double top,
{
Matrix result = { 0 };
- float rl = (right - left);
- float tb = (top - bottom);
- float fn = (far - near);
+ float rl = (float)(right - left);
+ float tb = (float)(top - bottom);
+ float fn = (float)(far - near);
- result.m0 = (near*2.0f)/rl;
+ result.m0 = ((float) near*2.0f)/rl;
result.m1 = 0.0f;
result.m2 = 0.0f;
result.m3 = 0.0f;
result.m4 = 0.0f;
- result.m5 = (near*2.0f)/tb;
+ result.m5 = ((float) near*2.0f)/tb;
result.m6 = 0.0f;
result.m7 = 0.0f;
- result.m8 = (right + left)/rl;
- result.m9 = (top + bottom)/tb;
- result.m10 = -(far + near)/fn;
+ result.m8 = ((float)right + (float)left)/rl;
+ result.m9 = ((float)top + (float)bottom)/tb;
+ result.m10 = -((float)far + (float)near)/fn;
result.m11 = -1.0f;
result.m12 = 0.0f;
result.m13 = 0.0f;
- result.m14 = -(far*near*2.0f)/fn;
+ result.m14 = -((float)far*(float)near*2.0f)/fn;
result.m15 = 0.0f;
return result;
@@ -883,9 +883,9 @@ RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, d
{
Matrix result = { 0 };
- float rl = (right - left);
- float tb = (top - bottom);
- float fn = (far - near);
+ float rl = (float)(right - left);
+ float tb = (float)(top - bottom);
+ float fn = (float)(far - near);
result.m0 = 2.0f/rl;
result.m1 = 0.0f;
@@ -899,9 +899,9 @@ RMDEF Matrix MatrixOrtho(double left, double right, double bottom, double top, d
result.m9 = 0.0f;
result.m10 = -2.0f/fn;
result.m11 = 0.0f;
- result.m12 = -(left + right)/rl;
- result.m13 = -(top + bottom)/tb;
- result.m14 = -(far + near)/fn;
+ result.m12 = -((float)left + (float)right)/rl;
+ result.m13 = -((float)top + (float)bottom)/tb;
+ result.m14 = -((float)far + (float)near)/fn;
result.m15 = 1.0f;
return result;
@@ -980,7 +980,7 @@ RMDEF Quaternion QuaternionIdentity(void)
// Computes the length of a quaternion
RMDEF float QuaternionLength(Quaternion q)
{
- float result = sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
+ float result = (float)sqrt(q.x*q.x + q.y*q.y + q.z*q.z + q.w*q.w);
return result;
}
@@ -1071,8 +1071,8 @@ RMDEF Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount)
else if (cosHalfTheta > 0.95f) result = QuaternionNlerp(q1, q2, amount);
else
{
- float halfTheta = acos(cosHalfTheta);
- float sinHalfTheta = sqrt(1.0f - cosHalfTheta*cosHalfTheta);
+ float halfTheta = (float) acos(cosHalfTheta);
+ float sinHalfTheta = (float) sqrt(1.0f - cosHalfTheta*cosHalfTheta);
if (fabs(sinHalfTheta) < 0.001f)
{
diff --git a/src/rlgl.h b/src/rlgl.h
index d3b916ea..09f99ea2 100644
--- a/src/rlgl.h
+++ b/src/rlgl.h
@@ -3306,8 +3306,8 @@ Texture2D GenTexturePrefilter(Shader shader, Texture2D cubemap, int size)
for (unsigned int mip = 0; mip < MAX_MIPMAP_LEVELS; mip++)
{
// Resize framebuffer according to mip-level size.
- unsigned int mipWidth = size*powf(0.5f, mip);
- unsigned int mipHeight = size*powf(0.5f, mip);
+ unsigned int mipWidth = size*(int) powf(0.5f, (float) mip);
+ unsigned int mipHeight = size* (int) powf(0.5f, (float) mip);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, mipWidth, mipHeight);
@@ -3633,15 +3633,15 @@ void EndVrDrawing(void)
// Bottom-right corner for texture and quad
rlTexCoord2f(0.0f, 0.0f);
- rlVertex2f(0.0f, vrConfig.stereoFbo.texture.height);
+ rlVertex2f(0.0f, (float)vrConfig.stereoFbo.texture.height);
// Top-right corner for texture and quad
rlTexCoord2f(1.0f, 0.0f);
- rlVertex2f(vrConfig.stereoFbo.texture.width, vrConfig.stereoFbo.texture.height);
+ rlVertex2f( (float)vrConfig.stereoFbo.texture.width, (float)vrConfig.stereoFbo.texture.height);
// Top-left corner for texture and quad
rlTexCoord2f(1.0f, 1.0f);
- rlVertex2f(vrConfig.stereoFbo.texture.width, 0.0f);
+ rlVertex2f( (float)vrConfig.stereoFbo.texture.width, 0.0f);
rlEnd();
rlPopMatrix();
@@ -4502,7 +4502,7 @@ static void SetStereoConfig(VrDeviceInfo hmd)
// Compute distortion scale parameters
// NOTE: To get lens max radius, lensShift must be normalized to [-1..1]
- float lensRadius = fabs(-1.0f - 4.0f*lensShift);
+ float lensRadius = (float)fabs(-1.0f - 4.0f*lensShift);
float lensRadiusSq = lensRadius*lensRadius;
float distortionScale = hmd.lensDistortionValues[0] +
hmd.lensDistortionValues[1]*lensRadiusSq +
@@ -4553,8 +4553,8 @@ static void SetStereoConfig(VrDeviceInfo hmd)
vrConfig.eyesViewOffset[1] = MatrixTranslate(hmd.interpupillaryDistance*0.5f, 0.075f, 0.045f);
// Compute eyes Viewports
- vrConfig.eyesViewport[0] = (Rectangle){ 0, 0, hmd.hResolution/2, hmd.vResolution };
- vrConfig.eyesViewport[1] = (Rectangle){ hmd.hResolution/2, 0, hmd.hResolution/2, hmd.vResolution };
+ vrConfig.eyesViewport[0] = (Rectangle){ 0.0f, 0.0f, (float)hmd.hResolution/2, (float)hmd.vResolution };
+ vrConfig.eyesViewport[1] = (Rectangle){ hmd.hResolution/2.0f, 0.0f, (float)hmd.hResolution/2, (float) hmd.vResolution };
}
// Set internal projection and modelview matrix depending on eyes tracking data
diff --git a/src/shapes.c b/src/shapes.c
index 424fb0ba..833f1613 100644
--- a/src/shapes.c
+++ b/src/shapes.c
@@ -153,9 +153,9 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color)
for (int i = 1; i <= LINE_DIVISIONS; i++)
{
// Cubic easing in-out
- // NOTE: Easing is calcutated only for y position value
- current.y = EaseCubicInOut(i, startPos.y, endPos.y - startPos.y, LINE_DIVISIONS);
- current.x = previous.x + (endPos.x - startPos.x)/LINE_DIVISIONS;
+ // NOTE: Easing is calculated only for y position value
+ current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)LINE_DIVISIONS);
+ current.x = previous.x + (endPos.x - startPos.x)/ (float)LINE_DIVISIONS;
DrawLineEx(previous, current, thick, color);
@@ -324,7 +324,7 @@ void DrawRectangleV(Vector2 position, Vector2 size, Color color)
// Draw a color-filled rectangle
void DrawRectangleRec(Rectangle rec, Color color)
{
- DrawRectangle(rec.x, rec.y, rec.width, rec.height, color);
+ DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, color);
}
void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color)
@@ -354,14 +354,14 @@ void DrawRectanglePro(Rectangle rec, Vector2 origin, float rotation, Color color
// NOTE: Gradient goes from bottom (color1) to top (color2)
void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2)
{
- DrawRectangleGradientEx((Rectangle){ posX, posY, width, height }, color1, color2, color2, color1);
+ DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color2, color2, color1);
}
// Draw a horizontal-gradient-filled rectangle
// NOTE: Gradient goes from bottom (color1) to top (color2)
void DrawRectangleGradientH(int posX, int posY, int width, int height, Color color1, Color color2)
{
- DrawRectangleGradientEx((Rectangle){ posX, posY, width, height }, color1, color1, color2, color2);
+ DrawRectangleGradientEx((Rectangle){ (float)posX, (float)posY, (float)width, (float)height }, color1, color1, color2, color2);
}
// Draw a gradient-filled rectangle
@@ -457,14 +457,14 @@ void DrawRectangleLinesEx(Rectangle rec, int lineThick, Color color)
{
if (lineThick > rec.width || lineThick > rec.height)
{
- if(rec.width > rec.height) lineThick = rec.height/2;
- else if (rec.width < rec.height) lineThick = rec.width/2;
+ if(rec.width > rec.height) lineThick = (int)rec.height/2;
+ else if (rec.width < rec.height) lineThick = (int)rec.width/2;
}
- DrawRectangle(rec.x, rec.y, rec.width, lineThick, color);
- DrawRectangle(rec.x - lineThick + rec.width, rec.y + lineThick, lineThick, rec.height - lineThick*2, color);
- DrawRectangle(rec.x, rec.y + rec.height - lineThick, rec.width, lineThick, color);
- DrawRectangle(rec.x, rec.y + lineThick, lineThick, rec.height - lineThick*2, color);
+ DrawRectangle( (int)rec.x, (int)rec.y, (int)rec.width, lineThick, color);
+ DrawRectangle( (int)(rec.x - lineThick + rec.width), (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2.0f), color);
+ DrawRectangle( (int)rec.x, (int)(rec.y + rec.height - lineThick), (int)rec.width, lineThick, color);
+ DrawRectangle( (int)rec.x, (int)(rec.y + lineThick), lineThick, (int)(rec.height - lineThick*2), color);
}
// Draw a triangle
@@ -648,8 +648,8 @@ bool CheckCollisionRecs(Rectangle rec1, Rectangle rec2)
{
bool collision = false;
- float dx = fabs((rec1.x + rec1.width/2) - (rec2.x + rec2.width/2));
- float dy = fabs((rec1.y + rec1.height/2) - (rec2.y + rec2.height/2));
+ float dx = (float)fabs((rec1.x + rec1.width/2) - (rec2.x + rec2.width/2));
+ float dy = (float)fabs((rec1.y + rec1.height/2) - (rec2.y + rec2.height/2));
if ((dx <= (rec1.width/2 + rec2.width/2)) && ((dy <= (rec1.height/2 + rec2.height/2)))) collision = true;
@@ -675,11 +675,11 @@ bool CheckCollisionCircles(Vector2 center1, float radius1, Vector2 center2, floa
// NOTE: Reviewed version to take into account corner limit case
bool CheckCollisionCircleRec(Vector2 center, float radius, Rectangle rec)
{
- int recCenterX = rec.x + rec.width/2;
- int recCenterY = rec.y + rec.height/2;
+ int recCenterX = (int)(rec.x + rec.width/2.0f);
+ int recCenterY = (int)(rec.y + rec.height/2.0f);
- float dx = fabs(center.x - recCenterX);
- float dy = fabs(center.y - recCenterY);
+ float dx = (float)fabs(center.x - recCenterX);
+ float dy = (float)fabs(center.y - recCenterY);
if (dx > (rec.width/2.0f + radius)) { return false; }
if (dy > (rec.height/2.0f + radius)) { return false; }
@@ -700,8 +700,8 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
if (CheckCollisionRecs(rec1, rec2))
{
- float dxx = fabs(rec1.x - rec2.x);
- float dyy = fabs(rec1.y - rec2.y);
+ float dxx = (float)fabs(rec1.x - rec2.x);
+ float dyy = (float)fabs(rec1.y - rec2.y);
if (rec1.x <= rec2.x)
{
@@ -768,8 +768,8 @@ Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2)
// NOTE: Required for DrawLineBezier()
static float EaseCubicInOut(float t, float b, float c, float d)
{
- if ((t /= 0.5*d) < 1)
- return 0.5*c*t*t*t + b;
+ if ((t /= 0.5f*d) < 1)
+ return 0.5f*c*t*t*t + b;
t -= 2;
- return 0.5*c*(t*t*t + 2) + b;
+ return 0.5f*c*(t*t*t + 2.0f) + b;
}
diff --git a/src/text.c b/src/text.c
index e871a6aa..9ed283b2 100644
--- a/src/text.c
+++ b/src/text.c
@@ -220,12 +220,12 @@ extern void LoadDefaultFont(void)
{
defaultFont.chars[i].value = 32 + i; // First char is 32
- defaultFont.chars[i].rec.x = currentPosX;
- defaultFont.chars[i].rec.y = charsDivisor + currentLine*(charsHeight + charsDivisor);
- defaultFont.chars[i].rec.width = charsWidth[i];
- defaultFont.chars[i].rec.height = charsHeight;
+ defaultFont.chars[i].rec.x = (float)currentPosX;
+ defaultFont.chars[i].rec.y = (float)charsDivisor + currentLine*(charsHeight + charsDivisor);
+ defaultFont.chars[i].rec.width = (float)charsWidth[i];
+ defaultFont.chars[i].rec.height = (float)charsHeight;
- testPosX += (defaultFont.chars[i].rec.width + charsDivisor);
+ testPosX += (int)(defaultFont.chars[i].rec.width + (float)charsDivisor);
if (testPosX >= defaultFont.texture.width)
{
@@ -233,8 +233,8 @@ extern void LoadDefaultFont(void)
currentPosX = 2*charsDivisor + charsWidth[i];
testPosX = currentPosX;
- defaultFont.chars[i].rec.x = charsDivisor;
- defaultFont.chars[i].rec.y = charsDivisor + currentLine*(charsHeight + charsDivisor);
+ defaultFont.chars[i].rec.x = (float)charsDivisor;
+ defaultFont.chars[i].rec.y = (float)charsDivisor + currentLine*(charsHeight + charsDivisor);
}
else currentPosX = testPosX;
@@ -244,7 +244,7 @@ extern void LoadDefaultFont(void)
defaultFont.chars[i].advanceX = 0;
}
- defaultFont.baseSize = defaultFont.chars[0].rec.height;
+ defaultFont.baseSize = (int) defaultFont.chars[0].rec.height;
TraceLog(LOG_INFO, "[TEX ID %i] Default font loaded successfully", defaultFont.texture.id);
}
@@ -361,14 +361,14 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
if (!stbtt_InitFont(&fontInfo, fontBuffer, 0)) TraceLog(LOG_WARNING, "Failed to init font!");
// Calculate font scale factor
- float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, fontSize);
+ float scaleFactor = stbtt_ScaleForPixelHeight(&fontInfo, (float)fontSize);
// Calculate font basic metrics
// NOTE: ascent is equivalent to font baseline
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
- ascent *= scaleFactor;
- descent *= scaleFactor;
+ ascent *= (int) scaleFactor;
+ descent *= (int) scaleFactor;
// Fill fontChars in case not provided externally
// NOTE: By default we fill charsCount consecutevely, starting at 32 (Space)
@@ -407,7 +407,7 @@ CharInfo *LoadFontData(const char *fileName, int fontSize, int *fontChars, int c
TraceLog(LOG_DEBUG, "Character offsetY: %i", ascent + chY1);
stbtt_GetCodepointHMetrics(&fontInfo, ch, &chars[i].advanceX, NULL);
- chars[i].advanceX *= scaleFactor;
+ chars[i].advanceX *= (int) scaleFactor;
}
free(fontBuffer);
@@ -460,8 +460,8 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
}
}
- chars[i].rec.x = offsetX;
- chars[i].rec.y = offsetY;
+ chars[i].rec.x = (float)offsetX;
+ chars[i].rec.y = (float)offsetY;
// Move atlas position X for next character drawing
offsetX += ((int)chars[i].rec.width + 2*padding);
@@ -502,8 +502,8 @@ Image GenImageFontAtlas(CharInfo *chars, int charsCount, int fontSize, int paddi
for (int i = 0; i < charsCount; i++)
{
- chars[i].rec.x = rects[i].x + padding;
- chars[i].rec.y = rects[i].y + padding;
+ chars[i].rec.x = rects[i].x + (float)padding;
+ chars[i].rec.y = rects[i].y + (float)padding;
if (rects[i].was_packed)
{
@@ -834,15 +834,15 @@ static Font LoadImageFont(Image image, Color key, int firstChar)
{
tempCharValues[index] = firstChar + index;
- tempCharRecs[index].x = xPosToRead;
- tempCharRecs[index].y = lineSpacing + lineToRead*(charHeight + lineSpacing);
- tempCharRecs[index].height = charHeight;
+ tempCharRecs[index].x = (float)xPosToRead;
+ tempCharRecs[index].y = (float)(lineSpacing + lineToRead*(charHeight + lineSpacing));
+ tempCharRecs[index].height = (float)charHeight;
int charWidth = 0;
while (!COLOR_EQUAL(pixels[(lineSpacing + (charHeight+lineSpacing)*lineToRead)*image.width + xPosToRead + charWidth], key)) charWidth++;
- tempCharRecs[index].width = charWidth;
+ tempCharRecs[index].width = (float)charWidth;
index++;
@@ -887,7 +887,7 @@ static Font LoadImageFont(Image image, Color key, int firstChar)
spriteFont.chars[i].advanceX = 0;
}
- spriteFont.baseSize = spriteFont.chars[0].rec.height;
+ spriteFont.baseSize = (int) spriteFont.chars[0].rec.height;
TraceLog(LOG_INFO, "Image file loaded correctly as Font");
@@ -996,7 +996,7 @@ static Font LoadBMFont(const char *fileName)
// Save data properly in sprite font
font.chars[i].value = charId;
- font.chars[i].rec = (Rectangle){ charX, charY, charWidth, charHeight };
+ font.chars[i].rec = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight };
font.chars[i].offsetX = charOffsetX;
font.chars[i].offsetY = charOffsetY;
font.chars[i].advanceX = charAdvanceX;
diff --git a/src/textures.c b/src/textures.c
index 48bf69ac..700b4be9 100644
--- a/src/textures.c
+++ b/src/textures.c
@@ -314,7 +314,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int
// NOTE: fread() returns num read elements instead of bytes,
// to get bytes we need to read (1 byte size, elements) instead of (x byte size, 1 element)
- int bytes = fread(image.data, 1, size, rawFile);
+ size_t bytes = fread(image.data, 1, size, rawFile);
// Check if data has been read successfully
if (bytes < size)
@@ -1051,16 +1051,16 @@ void ImageAlphaCrop(Image *image, float threshold)
minx = i%image->width;
miny = -(-((i/image->width) + 1) + 1);
- if (crop.y == 0) crop.y = miny;
+ if (crop.y == 0.0f) crop.y = (float)miny;
- if (crop.x == 0) crop.x = minx;
- else if (minx < crop.x) crop.x = minx;
+ if (crop.x == 0.0f) crop.x = (float)minx;
+ else if (minx < crop.x) crop.x = (float)minx;
- if (crop.width == 0) crop.width = minx;
- else if (crop.width < minx) crop.width = minx;
+ if (crop.width == 0.0f) crop.width = (float)minx;
+ else if (crop.width < minx) crop.width = (float)minx;
- if (crop.height == 0) crop.height = miny;
- else if (crop.height < miny) crop.height = miny;
+ if (crop.height == 0.0f) crop.height = (float)miny;
+ else if (crop.height < (float)miny) crop.height = (float)miny;
}
}
@@ -1216,8 +1216,8 @@ void ImageResizeNN(Image *image,int newWidth,int newHeight)
void ImageResizeCanvas(Image *image, int newWidth,int newHeight, int offsetX, int offsetY, Color color)
{
Image imTemp = GenImageColor(newWidth, newHeight, color);
- Rectangle srcRec = { 0, 0, image->width, image->height };
- Rectangle dstRec = { offsetX, offsetY, srcRec.width, srcRec.height };
+ Rectangle srcRec = { 0.0f, 0.0f, (float)image->width, (float)image->height };
+ Rectangle dstRec = { (float)offsetX, (float)offsetY, (float)srcRec.width, (float)srcRec.height };
// TODO: Review different scaling situations
@@ -1450,7 +1450,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec)
if (dstRec.y < 0) dstRec.y = 0;
// Scale source image in case destination rec size is different than source rec size
- if ((dstRec.width != srcRec.width) || (dstRec.height != srcRec.height)) ImageResize(&srcCopy, dstRec.width, dstRec.height);
+ if ((dstRec.width != srcRec.width) || (dstRec.height != srcRec.height)) ImageResize(&srcCopy, (int)dstRec.width, (int)dstRec.height);
if ((dstRec.x + dstRec.width) > dst->width)
{
@@ -1530,7 +1530,7 @@ Image ImageText(const char *text, int fontSize, Color color)
{
int defaultFontSize = 10; // Default Font chars height in pixel
if (fontSize < defaultFontSize) fontSize = defaultFontSize;
- int spacing = (float)fontSize/defaultFontSize;
+ int spacing = fontSize / defaultFontSize;
Image imText = ImageTextEx(GetFontDefault(), text, (float)fontSize, (float)spacing, color);
@@ -1546,7 +1546,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
unsigned char character; // Current character
// TODO: ISSUE: Measured text size does not seem to be correct... issue on ImageDraw()
- Vector2 imSize = MeasureTextEx(font, text, font.baseSize, spacing);
+ Vector2 imSize = MeasureTextEx(font, text, (float)font.baseSize, spacing);
TraceLog(LOG_DEBUG, "Text Image size: %f, %f", imSize.x, imSize.y);
@@ -1588,12 +1588,12 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
if ((unsigned char)text[i] != ' ')
{
- ImageDraw(&imText, imFont, letter.rec, (Rectangle){ posX + letter.offsetX,
- letter.offsetY, letter.rec.width, letter.rec.height });
+ ImageDraw(&imText, imFont, letter.rec, (Rectangle){ (float)(posX + letter.offsetX),
+ (float)letter.offsetY, (float)letter.rec.width, (float)letter.rec.height });
}
- if (letter.advanceX == 0) posX += letter.rec.width + spacing;
- else posX += letter.advanceX + spacing;
+ if (letter.advanceX == 0) posX += (int)(letter.rec.width + spacing);
+ else posX += letter.advanceX + (int)spacing;
}
}
@@ -1616,9 +1616,9 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co
// Draw rectangle within an image
void ImageDrawRectangle(Image *dst, Vector2 position, Rectangle rec, Color color)
{
- Image imRec = GenImageColor(rec.width, rec.height, color);
+ Image imRec = GenImageColor((int)rec.width, (int)rec.height, color);
- Rectangle dstRec = { position.x, position.y, imRec.width, imRec.height };
+ Rectangle dstRec = { position.x, position.y, (float)imRec.width, (float)imRec.height };
ImageDraw(dst, imRec, rec, dstRec);
@@ -1637,8 +1637,8 @@ void ImageDrawTextEx(Image *dst, Vector2 position, Font font, const char *text,
{
Image imText = ImageTextEx(font, text, fontSize, spacing, color);
- Rectangle srcRec = { 0, 0, imText.width, imText.height };
- Rectangle dstRec = { position.x, position.y, imText.width, imText.height };
+ Rectangle srcRec = { 0.0f, 0.0f, (float)imText.width, (float)imText.height };
+ Rectangle dstRec = { position.x, position.y, (float)imText.width, (float)imText.height };
ImageDraw(dst, imText, srcRec, dstRec);
@@ -1759,10 +1759,11 @@ void ImageColorTint(Image *image, Color color)
{
for (int x = 0; x < image->width; x++)
{
- unsigned char r = 255*((float)pixels[y*image->width + x].r/255*cR);
- unsigned char g = 255*((float)pixels[y*image->width + x].g/255*cG);
- unsigned char b = 255*((float)pixels[y*image->width + x].b/255*cB);
- unsigned char a = 255*((float)pixels[y*image->width + x].a/255*cA);
+ int index = y * image->width + x;
+ unsigned char r = 255*((float)pixels[index].r/255*cR);
+ unsigned char g = 255*((float)pixels[index].g/255*cG);
+ unsigned char b = 255*((float)pixels[index].b/255*cB);
+ unsigned char a = 255*((float)pixels[index].a/255*cA);
pixels[y*image->width + x].r = r;
pixels[y*image->width + x].g = g;
@@ -2010,8 +2011,8 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner,
float dist = hypotf((float)x - centerX, (float)y - centerY);
float factor = (dist - radius*density)/(radius*(1.0f - density));
- factor = fmax(factor, 0.f);
- factor = fmin(factor, 1.f); // dist can be bigger than radius so we have to check
+ factor = (float)fmax(factor, 0.f);
+ factor = (float)fmin(factor, 1.f); // dist can be bigger than radius so we have to check
pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor));
pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor));
@@ -2109,7 +2110,7 @@ Image GenImageCellular(int width, int height, int tileSize)
{
int y = (i/seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
int x = (i%seedsPerRow)*tileSize + GetRandomValue(0, tileSize - 1);
- seeds[i] = (Vector2){x, y};
+ seeds[i] = (Vector2){ (float)x, (float)y};
}
for (int y = 0; y < height; y++)
@@ -2120,7 +2121,7 @@ Image GenImageCellular(int width, int height, int tileSize)
{
int tileX = x/tileSize;
- float minDistance = strtod("Inf", NULL);
+ float minDistance = (float)strtod("Inf", NULL);
// Check all adjacent tiles
for (int i = -1; i < 2; i++)
@@ -2133,8 +2134,8 @@ Image GenImageCellular(int width, int height, int tileSize)
Vector2 neighborSeed = seeds[(tileY + j)*seedsPerRow + tileX + i];
- float dist = hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
- minDistance = fmin(minDistance, dist);
+ float dist = (float)hypot(x - (int)neighborSeed.x, y - (int)neighborSeed.y);
+ minDistance = (float)fmin(minDistance, dist);
}
}
@@ -2278,7 +2279,7 @@ void DrawTextureEx(Texture2D texture, Vector2 position, float rotation, float sc
// Draw a part of a texture (defined by a rectangle)
void DrawTextureRec(Texture2D texture, Rectangle sourceRec, Vector2 position, Color tint)
{
- Rectangle destRec = { position.x, position.y, sourceRec.width, fabs(sourceRec.height) };
+ Rectangle destRec = { position.x, position.y, sourceRec.width, (float)fabs(sourceRec.height) };
Vector2 origin = { 0.0f, 0.0f };
DrawTexturePro(texture, sourceRec, destRec, origin, 0.0f, tint);
@@ -2880,7 +2881,7 @@ static Image LoadKTX(const char *fileName)
if (ktxHeader.keyValueDataSize > 0)
{
- for (int i = 0; i < ktxHeader.keyValueDataSize; i++) fread(&unused, sizeof(unsigned char), 1, ktxFile);
+ for (unsigned int i = 0; i < ktxHeader.keyValueDataSize; i++) fread(&unused, sizeof(unsigned char), 1U, ktxFile);
}
int dataSize;